diff --git a/webclient/room/room-controller.js b/syweb/webclient/room/room-controller.js
index 841b5cccdd..67372a804f 100644
--- a/webclient/room/room-controller.js
+++ b/syweb/webclient/room/room-controller.js
@@ -14,12 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
-.controller('RoomController', ['$modal', '$filter', '$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'mPresence', 'eventHandlerService', 'mFileUpload', 'matrixPhoneService', 'MatrixCall',
- function($modal, $filter, $scope, $timeout, $routeParams, $location, $rootScope, matrixService, mPresence, eventHandlerService, mFileUpload, matrixPhoneService, MatrixCall) {
+angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput', 'angular-peity'])
+.controller('RoomController', ['$modal', '$filter', '$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'mPresence', 'eventHandlerService', 'mFileUpload', 'matrixPhoneService', 'MatrixCall', 'modelService', 'recentsService', 'commandsService',
+ function($modal, $filter, $scope, $timeout, $routeParams, $location, $rootScope, matrixService, mPresence, eventHandlerService, mFileUpload, matrixPhoneService, MatrixCall, modelService, recentsService, commandsService) {
'use strict';
var MESSAGES_PER_PAGINATION = 30;
var THUMBNAIL_SIZE = 320;
+
+ // .html needs this
+ $scope.containsBingWord = eventHandlerService.eventContainsBingWord;
// Room ids. Computed and resolved in onInit
$scope.room_id = undefined;
@@ -36,12 +39,8 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
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;
- $scope.autoCompleteIndex = 0;
- $scope.autoCompleteOriginal = "";
$scope.imageURLToSend = "";
- $scope.userIDToInvite = "";
// vars and functions for updating the name
@@ -54,7 +53,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
return;
};
- var nameEvent = $rootScope.events.rooms[$scope.room_id]['m.room.name'];
+ var nameEvent = $scope.room.current_room_state.state_events['m.room.name'];
if (nameEvent) {
$scope.name.newNameText = nameEvent.content.name;
}
@@ -95,7 +94,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
console.log("Warning: Already editing topic.");
return;
}
- var topicEvent = $rootScope.events.rooms[$scope.room_id]['m.room.topic'];
+ var topicEvent = $scope.room.current_room_state.state_events['m.room.topic'];
if (topicEvent) {
$scope.topic.newTopicText = topicEvent.content.topic;
}
@@ -152,7 +151,6 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
$scope.$on(eventHandlerService.MSG_EVENT, function(ngEvent, event, isLive) {
if (isLive && event.room_id === $scope.room_id) {
-
scrollToBottom();
}
});
@@ -187,21 +185,6 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
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) {
- 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);
- }
}
}
});
@@ -240,11 +223,11 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
$scope.state.paginating = true;
}
- console.log("paginateBackMessages from " + $rootScope.events.rooms[$scope.room_id].pagination.earliest_token + " for " + numItems);
+ console.log("paginateBackMessages from " + $scope.room.old_room_state.pagination_token + " for " + numItems);
var originalTopRow = $("#messageTable>tbody>tr:first")[0];
// Paginate events from the point in cache
- matrixService.paginateBackMessages($scope.room_id, $rootScope.events.rooms[$scope.room_id].pagination.earliest_token, numItems).then(
+ matrixService.paginateBackMessages($scope.room_id, $scope.room.old_room_state.pagination_token, numItems).then(
function(response) {
eventHandlerService.handleRoomMessages($scope.room_id, response.data, false, 'b');
@@ -327,8 +310,9 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
}
$scope.members[target_user_id] = chunk;
- if (target_user_id in $rootScope.presence) {
- updatePresence($rootScope.presence[target_user_id]);
+ var usr = modelService.getUser(target_user_id);
+ if (usr) {
+ updatePresence(usr.event);
}
}
else {
@@ -390,7 +374,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
var updateUserPowerLevel = function(user_id) {
var member = $scope.members[user_id];
if (member) {
- member.powerLevel = matrixService.getUserPowerLevel($scope.room_id, user_id);
+ member.powerLevel = eventHandlerService.getUserPowerLevel($scope.room_id, user_id);
normaliseMembersPowerLevels();
}
@@ -431,172 +415,25 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
scrollToBottom(true);
// Store the command in the history
- history.push(input);
+ $rootScope.$broadcast("commandHistory:BROADCAST_NEW_HISTORY_ITEM(item)",
+ input);
+ var isEmote = input.indexOf("/me ") === 0;
var promise;
- var cmd;
- var args;
+ if (!isEmote) {
+ promise = commandsService.processInput($scope.room_id, input);
+ }
var echo = false;
- // Check for IRC style commands first
- // trim any trailing whitespace, as it can confuse the parser for IRC-style commands
- input = input.replace(/\s+$/, "");
-
- if (input[0] === "/" && input[1] !== "/") {
- var bits = input.match(/^(\S+?)( +(.*))?$/);
- cmd = bits[1];
- args = bits[3];
-
- console.log("cmd: " + cmd + ", args: " + args);
-
- switch (cmd) {
- case "/me":
- promise = matrixService.sendEmoteMessage($scope.room_id, args);
- echo = true;
- break;
-
- case "/nick":
- // Change user display name
- if (args) {
- promise = matrixService.setDisplayName(args);
- }
- else {
- $scope.feedback = "Usage: /nick <display_name>";
- }
- break;
-
- case "/join":
- // Join a room
- if (args) {
- var matches = args.match(/^(\S+)$/);
- if (matches) {
- var room_alias = matches[1];
- if (room_alias.indexOf(':') == -1) {
- // FIXME: actually track the :domain style name of our homeserver
- // with or without port as is appropriate and append it at this point
- }
-
- var room_id = matrixService.getAliasToRoomIdMapping(room_alias);
- console.log("joining " + room_alias + " id=" + room_id);
- if ($rootScope.events.rooms[room_id]) {
- // don't send a join event for a room you're already in.
- $location.url("room/" + room_alias);
- }
- else {
- promise = matrixService.joinAlias(room_alias).then(
- function(response) {
- // TODO: factor out the common housekeeping whenever we try to join a room or alias
- matrixService.roomState(response.room_id).then(
- function(response) {
- eventHandlerService.handleEvents(response.data, false, true);
- },
- function(error) {
- $scope.feedback = "Failed to get room state for: " + response.room_id;
- }
- );
- $location.url("room/" + room_alias);
- },
- function(error) {
- $scope.feedback = "Can't join room: " + JSON.stringify(error.data);
- }
- );
- }
- }
- }
- else {
- $scope.feedback = "Usage: /join <room_alias>";
- }
- break;
-
- case "/kick":
- // Kick a user from the room with an optional reason
- if (args) {
- var matches = args.match(/^(\S+?)( +(.*))?$/);
- if (matches) {
- promise = matrixService.kick($scope.room_id, matches[1], matches[3]);
- }
- }
-
- if (!promise) {
- $scope.feedback = "Usage: /kick <userId> [<reason>]";
- }
- break;
-
- case "/ban":
- // 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]);
- }
- }
-
- if (!promise) {
- $scope.feedback = "Usage: /ban <userId> [<reason>]";
- }
- break;
-
- case "/unban":
- // Unban a user from the room
- if (args) {
- var matches = args.match(/^(\S+)$/);
- if (matches) {
- // Reset the user membership to "leave" to unban him
- promise = matrixService.unban($scope.room_id, matches[1]);
- }
- }
-
- if (!promise) {
- $scope.feedback = "Usage: /unban <userId>";
- }
- break;
-
- case "/op":
- // Define the power level of a user
- 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 && undefined !== matches[3]) {
- powerLevel = parseInt(matches[3]);
- }
- if (powerLevel !== NaN) {
- promise = matrixService.setUserPowerLevel($scope.room_id, user_id, powerLevel);
- }
- }
- }
-
- if (!promise) {
- $scope.feedback = "Usage: /op <userId> [<power level>]";
- }
- break;
-
- case "/deop":
- // Reset the power level of a user
- if (args) {
- var matches = args.match(/^(\S+)$/);
- if (matches) {
- promise = matrixService.setUserPowerLevel($scope.room_id, args, undefined);
- }
- }
-
- if (!promise) {
- $scope.feedback = "Usage: /deop <userId>";
- }
- break;
-
- default:
- $scope.feedback = ("Unrecognised IRC-style command: " + cmd);
- break;
- }
- }
- // By default send this as a message unless it's an IRC-style command
- if (!promise && !cmd) {
- // Make the request
- promise = matrixService.sendTextMessage($scope.room_id, input);
+ if (!promise) { // not a non-echoable command
echo = true;
+ if (isEmote) {
+ promise = matrixService.sendEmoteMessage($scope.room_id, input.substring(4));
+ }
+ else {
+ promise = matrixService.sendTextMessage($scope.room_id, input);
+ }
}
if (echo) {
@@ -604,8 +441,8 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
// 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: (cmd === "/me" ? args : input),
- msgtype: (cmd === "/me" ? "m.emote" : "m.text"),
+ body: (isEmote ? input.substring(4) : input),
+ msgtype: (isEmote ? "m.emote" : "m.text"),
},
origin_server_ts: new Date().getTime(), // fake a timestamp
room_id: $scope.room_id,
@@ -615,7 +452,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
};
$('#mainInput').val('');
- $rootScope.events.rooms[$scope.room_id].messages.push(echoMessage);
+ $scope.room.addMessageEvent(echoMessage);
scrollToBottom();
}
@@ -638,7 +475,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
}
},
function(error) {
- $scope.feedback = "Request failed: " + error.data.error;
+ $scope.feedback = error.data.error;
if (echoMessage) {
// Mark the message as unsent for the rest of the page life
@@ -661,7 +498,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
if (room_id_or_alias && '!' === room_id_or_alias[0]) {
// Yes. We can go on right now
$scope.room_id = room_id_or_alias;
- $scope.room_alias = matrixService.getRoomIdToAliasMapping($scope.room_id);
+ $scope.room_alias = modelService.getRoomIdToAliasMapping($scope.room_id);
onInit2();
}
else {
@@ -703,6 +540,9 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
var onInit2 = function() {
console.log("onInit2");
+ // =============================
+ $scope.room = modelService.getRoom($scope.room_id);
+ // =============================
// Scroll down as soon as possible so that we point to the last message
// if it already exists in memory
@@ -715,9 +555,9 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
var needsToJoin = true;
// The room members is available in the data fetched by initialSync
- if ($rootScope.events.rooms[$scope.room_id]) {
+ if ($scope.room) {
- var messages = $rootScope.events.rooms[$scope.room_id].messages;
+ var messages = $scope.room.events;
if (0 === messages.length
|| (1 === messages.length && "m.room.member" === messages[0].type && "invite" === messages[0].content.membership && $scope.state.user_id === messages[0].state_key)) {
@@ -729,19 +569,19 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
$scope.state.first_pagination = false;
}
- var members = $rootScope.events.rooms[$scope.room_id].members;
+ var members = $scope.room.current_room_state.members;
// Update the member list
for (var i in members) {
if (!members.hasOwnProperty(i)) continue;
- var member = members[i];
+ var member = members[i].event;
updateMemberList(member);
}
// Check if the user has already join the room
if ($scope.state.user_id in members) {
- if ("join" === members[$scope.state.user_id].membership) {
+ if ("join" === members[$scope.state.user_id].event.content.membership) {
needsToJoin = false;
}
}
@@ -785,10 +625,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
console.log("onInit3");
// Make recents highlight the current room
- $scope.recentsSelectedRoomID = $scope.room_id;
-
- // Init the history for this room
- history.init();
+ recentsService.setSelectedRoomId($scope.room_id);
// Get the up-to-date the current member list
matrixService.getMemberList($scope.room_id).then(
@@ -822,19 +659,6 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
}
);
};
-
- $scope.inviteUser = function() {
-
- matrixService.invite($scope.room_id, $scope.userIDToInvite).then(
- function() {
- console.log("Invited.");
- $scope.feedback = "Invite successfully sent to " + $scope.userIDToInvite;
- $scope.userIDToInvite = "";
- },
- function(reason) {
- $scope.feedback = "Failure: " + reason.data.error;
- });
- };
$scope.leaveRoom = function() {
@@ -886,109 +710,51 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
paginate(MESSAGES_PER_PAGINATION);
};
- $scope.startVoiceCall = function() {
+ $scope.checkWebRTC = function() {
+ if (!$rootScope.isWebRTCSupported()) {
+ alert("Your browser does not support WebRTC");
+ return false;
+ }
+ if ($scope.memberCount() != 2) {
+ alert("WebRTC calls are currently only supported on rooms with two members");
+ return false;
+ }
+ return true;
+ };
+
+ $scope.startVoiceCall = function() {
+ if (!$scope.checkWebRTC()) return;
var call = new MatrixCall($scope.room_id);
call.onError = $rootScope.onCallError;
call.onHangup = $rootScope.onCallHangup;
// remote video element is used for playing audio in voice calls
- call.remoteVideoElement = angular.element('#remoteVideo')[0];
+ call.remoteVideoSelector = angular.element('#remoteVideo')[0];
call.placeVoiceCall();
$rootScope.currentCall = call;
};
$scope.startVideoCall = function() {
+ if (!$scope.checkWebRTC()) return;
+
var call = new MatrixCall($scope.room_id);
call.onError = $rootScope.onCallError;
call.onHangup = $rootScope.onCallHangup;
- call.localVideoElement = angular.element('#localVideo')[0];
- call.remoteVideoElement = angular.element('#remoteVideo')[0];
+ call.localVideoSelector = '#localVideo';
+ call.remoteVideoSelector = '#remoteVideo';
call.placeVideoCall();
$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 = $('#mainInput').val();
- }
- else {
- // If the user modified this line in history, keep the change
- this.data[this.position] = $('#mainInput').val();
- }
-
- // 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
- $('#mainInput').val(this.data[this.position]);
- }
- else if (undefined !== this.typingMessage) {
- // Go back to the message the user started to type
- $('#mainInput').val(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();
- }
- };
-
$scope.openJson = function(content) {
- $scope.event_selected = content;
+ $scope.event_selected = angular.copy(content);
+
+ // FIXME: Pre-calculated event data should be stripped in a nicer way.
+ $scope.event_selected.__room_member = undefined;
+ $scope.event_selected.__target_room_member = undefined;
+
// scope this so the template can check power levels and enable/disable
// buttons
- $scope.pow = matrixService.getUserPowerLevel;
+ $scope.pow = eventHandlerService.getUserPowerLevel;
var modalInstance = $modal.open({
templateUrl: 'eventInfoTemplate.html',
@@ -1017,13 +783,70 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
});
};
+ $scope.openRoomInfo = function() {
+ $scope.roomInfo = {};
+ $scope.roomInfo.newEvent = {
+ content: {},
+ type: "",
+ state_key: ""
+ };
+
+ var stateEvents = $scope.room.current_room_state.state_events;
+ // 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);
+ " Redact level = "+$scope.room.current_room_state.state_events["m.room.ops_levels"].content.redact_level);
console.log("Redact event >> " + JSON.stringify($scope.event_selected));
$modalInstance.close("redact");
};
+ $scope.dismiss = $modalInstance.dismiss;
+})
+.controller('RoomInfoController', function($scope, $modalInstance, $filter, matrixService) {
+ console.log("Displaying room info.");
+
+ $scope.userIDToInvite = "";
+
+ $scope.inviteUser = function() {
+
+ matrixService.invite($scope.room_id, $scope.userIDToInvite).then(
+ function() {
+ console.log("Invited.");
+ $scope.feedback = "Invite successfully sent to " + $scope.userIDToInvite;
+ $scope.userIDToInvite = "";
+ },
+ function(reason) {
+ $scope.feedback = "Failure: " + reason.data.error;
+ });
+ };
+
+ $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;
+
});
|