mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2024-12-26 20:39:22 -05:00
Merge pull request #11 from matrix-org/webclient-room-data-restructure
Webclient room data restructure
This commit is contained in:
commit
020fc15d98
@ -21,8 +21,8 @@ limitations under the License.
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'eventStreamService'])
|
angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'eventStreamService'])
|
||||||
.controller('MatrixWebClientController', ['$scope', '$location', '$rootScope', '$timeout', '$animate', 'matrixService', 'mPresence', 'eventStreamService', 'eventHandlerService', 'matrixPhoneService',
|
.controller('MatrixWebClientController', ['$scope', '$location', '$rootScope', '$timeout', '$animate', 'matrixService', 'mPresence', 'eventStreamService', 'eventHandlerService', 'matrixPhoneService', 'modelService',
|
||||||
function($scope, $location, $rootScope, $timeout, $animate, matrixService, mPresence, eventStreamService, eventHandlerService, matrixPhoneService) {
|
function($scope, $location, $rootScope, $timeout, $animate, matrixService, mPresence, eventStreamService, eventHandlerService, matrixPhoneService, modelService) {
|
||||||
|
|
||||||
// Check current URL to avoid to display the logout button on the login page
|
// Check current URL to avoid to display the logout button on the login page
|
||||||
$scope.location = $location.path();
|
$scope.location = $location.path();
|
||||||
@ -117,7 +117,7 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var roomMembers = angular.copy($rootScope.events.rooms[$rootScope.currentCall.room_id].members);
|
var roomMembers = angular.copy(modelService.getRoom($rootScope.currentCall.room_id).current_room_state.members);
|
||||||
delete roomMembers[matrixService.config().user_id];
|
delete roomMembers[matrixService.config().user_id];
|
||||||
|
|
||||||
$rootScope.currentCall.user_id = Object.keys(roomMembers)[0];
|
$rootScope.currentCall.user_id = Object.keys(roomMembers)[0];
|
||||||
|
@ -31,6 +31,7 @@ var matrixWebClient = angular.module('matrixWebClient', [
|
|||||||
'eventStreamService',
|
'eventStreamService',
|
||||||
'eventHandlerService',
|
'eventHandlerService',
|
||||||
'notificationService',
|
'notificationService',
|
||||||
|
'modelService',
|
||||||
'infinite-scroll',
|
'infinite-scroll',
|
||||||
'ui.bootstrap',
|
'ui.bootstrap',
|
||||||
'monospaced.elastic'
|
'monospaced.elastic'
|
||||||
|
@ -22,13 +22,12 @@ not care where the event came from, it only needs enough context to be able to
|
|||||||
process them. Events may be coming from the event stream, the REST API (via
|
process them. Events may be coming from the event stream, the REST API (via
|
||||||
direct GETs or via a pagination stream API), etc.
|
direct GETs or via a pagination stream API), etc.
|
||||||
|
|
||||||
Typically, this service will store events or broadcast them to any listeners
|
Typically, this service will store events and broadcast them to any listeners
|
||||||
(e.g. controllers) via $broadcast. Alternatively, it may update the $rootScope
|
(e.g. controllers) via $broadcast.
|
||||||
if typically all the $on method would do is update its own $scope.
|
|
||||||
*/
|
*/
|
||||||
angular.module('eventHandlerService', [])
|
angular.module('eventHandlerService', [])
|
||||||
.factory('eventHandlerService', ['matrixService', '$rootScope', '$q', '$timeout', 'mPresence', 'notificationService',
|
.factory('eventHandlerService', ['matrixService', '$rootScope', '$q', '$timeout', 'mPresence', 'notificationService', 'modelService',
|
||||||
function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService) {
|
function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService, modelService) {
|
||||||
var ROOM_CREATE_EVENT = "ROOM_CREATE_EVENT";
|
var ROOM_CREATE_EVENT = "ROOM_CREATE_EVENT";
|
||||||
var MSG_EVENT = "MSG_EVENT";
|
var MSG_EVENT = "MSG_EVENT";
|
||||||
var MEMBER_EVENT = "MEMBER_EVENT";
|
var MEMBER_EVENT = "MEMBER_EVENT";
|
||||||
@ -44,6 +43,7 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
// of the app, given we never try to reap memory yet)
|
// of the app, given we never try to reap memory yet)
|
||||||
var eventMap = {};
|
var eventMap = {};
|
||||||
|
|
||||||
|
// TODO: Remove this and replace with modelService.User objects.
|
||||||
$rootScope.presence = {};
|
$rootScope.presence = {};
|
||||||
|
|
||||||
var initialSyncDeferred;
|
var initialSyncDeferred;
|
||||||
@ -51,81 +51,43 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
var reset = function() {
|
var reset = function() {
|
||||||
initialSyncDeferred = $q.defer();
|
initialSyncDeferred = $q.defer();
|
||||||
|
|
||||||
$rootScope.events = {
|
|
||||||
rooms: {} // will contain roomId: { messages:[], members:{userid1: event} }
|
|
||||||
};
|
|
||||||
|
|
||||||
$rootScope.presence = {};
|
$rootScope.presence = {};
|
||||||
|
|
||||||
eventMap = {};
|
eventMap = {};
|
||||||
};
|
};
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
var initRoom = function(room_id, room) {
|
|
||||||
if (!(room_id in $rootScope.events.rooms)) {
|
|
||||||
console.log("Creating new rooms entry for " + room_id);
|
|
||||||
$rootScope.events.rooms[room_id] = {
|
|
||||||
room_id: room_id,
|
|
||||||
messages: [],
|
|
||||||
members: {},
|
|
||||||
// Pagination information
|
|
||||||
pagination: {
|
|
||||||
earliest_token: "END" // how far back we've paginated
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (room) { // we got an existing room object from initialsync, seemingly.
|
|
||||||
// Report all other metadata of the room object (membership, inviter, visibility, ...)
|
|
||||||
for (var field in room) {
|
|
||||||
if (!room.hasOwnProperty(field)) continue;
|
|
||||||
|
|
||||||
if (-1 === ["room_id", "messages", "state"].indexOf(field)) { // why indexOf - why not ===? --Matthew
|
|
||||||
$rootScope.events.rooms[room_id][field] = room[field];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$rootScope.events.rooms[room_id].membership = room.membership;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var resetRoomMessages = function(room_id) {
|
var resetRoomMessages = function(room_id) {
|
||||||
if ($rootScope.events.rooms[room_id]) {
|
var room = modelService.getRoom(room_id);
|
||||||
$rootScope.events.rooms[room_id].messages = [];
|
room.events = [];
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generic method to handle events data
|
// Generic method to handle events data
|
||||||
var handleRoomDateEvent = function(event, isLiveEvent, addToRoomMessages) {
|
var handleRoomStateEvent = function(event, isLiveEvent, addToRoomMessages) {
|
||||||
// Add topic changes as if they were a room message
|
var room = modelService.getRoom(event.room_id);
|
||||||
if (addToRoomMessages) {
|
if (addToRoomMessages) {
|
||||||
if (isLiveEvent) {
|
// some state events are displayed as messages, so add them.
|
||||||
$rootScope.events.rooms[event.room_id].messages.push(event);
|
room.addMessageEvent(event, !isLiveEvent);
|
||||||
}
|
|
||||||
else {
|
|
||||||
$rootScope.events.rooms[event.room_id].messages.unshift(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// live events always update, but non-live events only update if the
|
if (isLiveEvent) {
|
||||||
// ts is later.
|
// update the current room state with the latest state
|
||||||
var latestData = true;
|
room.current_room_state.storeStateEvent(event);
|
||||||
if (!isLiveEvent) {
|
}
|
||||||
|
else {
|
||||||
var eventTs = event.origin_server_ts;
|
var eventTs = event.origin_server_ts;
|
||||||
var storedEvent = $rootScope.events.rooms[event.room_id][event.type];
|
var storedEvent = room.current_room_state.getStateEvent(event.type, event.state_key);
|
||||||
if (storedEvent) {
|
if (storedEvent) {
|
||||||
if (storedEvent.origin_server_ts > eventTs) {
|
if (storedEvent.origin_server_ts < eventTs) {
|
||||||
// ignore it, we have a newer one already.
|
// the incoming event is newer, use it.
|
||||||
latestData = false;
|
room.current_room_state.storeStateEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (latestData) {
|
// TODO: handle old_room_state
|
||||||
$rootScope.events.rooms[event.room_id][event.type] = event;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var handleRoomCreate = function(event, isLiveEvent) {
|
var handleRoomCreate = function(event, isLiveEvent) {
|
||||||
// For now, we do not use the event data. Simply signal it to the app controllers
|
|
||||||
$rootScope.$broadcast(ROOM_CREATE_EVENT, event, isLiveEvent);
|
$rootScope.$broadcast(ROOM_CREATE_EVENT, event, isLiveEvent);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -133,35 +95,7 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
matrixService.createRoomIdToAliasMapping(event.room_id, event.content.aliases[0]);
|
matrixService.createRoomIdToAliasMapping(event.room_id, event.content.aliases[0]);
|
||||||
};
|
};
|
||||||
|
|
||||||
var handleMessage = function(event, isLiveEvent) {
|
var displayNotification = function(event) {
|
||||||
// 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") ) {
|
|
||||||
// Assume we've already echoed it. So, there is a fake event in the messages list of the room
|
|
||||||
// Replace this fake event by the true one
|
|
||||||
var index = getRoomEventIndex(event.room_id, event.event_id);
|
|
||||||
if (index) {
|
|
||||||
$rootScope.events.rooms[event.room_id].messages[index] = event;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$rootScope.events.rooms[event.room_id].messages.push(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$rootScope.events.rooms[event.room_id].messages.push(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.Notification && event.user_id != matrixService.config().user_id) {
|
if (window.Notification && event.user_id != matrixService.config().user_id) {
|
||||||
var shouldBing = notificationService.containsBingWord(
|
var shouldBing = notificationService.containsBingWord(
|
||||||
matrixService.config().user_id,
|
matrixService.config().user_id,
|
||||||
@ -191,7 +125,7 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
|
|
||||||
if (shouldBing && isIdle) {
|
if (shouldBing && isIdle) {
|
||||||
console.log("Displaying notification for "+JSON.stringify(event));
|
console.log("Displaying notification for "+JSON.stringify(event));
|
||||||
var member = getMember(event.room_id, event.user_id);
|
var member = modelService.getMember(event.room_id, event.user_id);
|
||||||
var displayname = getUserDisplayName(event.room_id, event.user_id);
|
var displayname = getUserDisplayName(event.room_id, event.user_id);
|
||||||
|
|
||||||
var message = event.content.body;
|
var message = event.content.body;
|
||||||
@ -203,9 +137,9 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
var roomTitle = matrixService.getRoomIdToAliasMapping(event.room_id);
|
var roomTitle = matrixService.getRoomIdToAliasMapping(event.room_id);
|
||||||
var theRoom = $rootScope.events.rooms[event.room_id];
|
var theRoom = modelService.getRoom(event.room_id);
|
||||||
if (!roomTitle && theRoom && theRoom["m.room.name"] && theRoom["m.room.name"].content) {
|
if (!roomTitle && theRoom.current_room_state.state("m.room.name") && theRoom.current_room_state.state("m.room.name").content) {
|
||||||
roomTitle = theRoom["m.room.name"].content.name;
|
roomTitle = theRoom.current_room_state.state("m.room.name").content.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!roomTitle) {
|
if (!roomTitle) {
|
||||||
@ -223,56 +157,92 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================
|
||||||
|
|
||||||
|
var room = modelService.getRoom(event.room_id);
|
||||||
|
|
||||||
|
if (event.user_id !== matrixService.config().user_id) {
|
||||||
|
room.addMessageEvent(event, !isLiveEvent);
|
||||||
|
displayNotification(event);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$rootScope.events.rooms[event.room_id].messages.unshift(event);
|
// we may have locally echoed this, so we should replace the event
|
||||||
|
// instead of just adding.
|
||||||
|
room.addOrReplaceMessageEvent(event, !isLiveEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO send delivery receipt if isLiveEvent
|
// TODO send delivery receipt if isLiveEvent
|
||||||
|
|
||||||
// $broadcast this, as controllers may want to do funky things such as
|
|
||||||
// scroll to the bottom, etc which cannot be expressed via simple $scope
|
|
||||||
// updates.
|
|
||||||
$rootScope.$broadcast(MSG_EVENT, event, isLiveEvent);
|
$rootScope.$broadcast(MSG_EVENT, event, isLiveEvent);
|
||||||
};
|
};
|
||||||
|
|
||||||
var handleRoomMember = function(event, isLiveEvent, isStateEvent) {
|
var handleRoomMember = function(event, isLiveEvent, isStateEvent) {
|
||||||
|
var room = modelService.getRoom(event.room_id);
|
||||||
|
|
||||||
// add membership changes as if they were a room message if something interesting changed
|
// did something change?
|
||||||
// Exception: Do not do this if the event is a room state event because such events already come
|
var memberChanges = undefined;
|
||||||
// as room messages events. Moreover, when they come as room messages events, they are relatively ordered
|
|
||||||
// with other other room messages
|
|
||||||
if (!isStateEvent) {
|
if (!isStateEvent) {
|
||||||
// could be a membership change, display name change, etc.
|
// could be a membership change, display name change, etc.
|
||||||
// Find out which one.
|
// Find out which one.
|
||||||
var memberChanges = undefined;
|
|
||||||
if ((event.prev_content === undefined && event.content.membership) || (event.prev_content && (event.prev_content.membership !== event.content.membership))) {
|
if ((event.prev_content === undefined && event.content.membership) || (event.prev_content && (event.prev_content.membership !== event.content.membership))) {
|
||||||
memberChanges = "membership";
|
memberChanges = "membership";
|
||||||
}
|
}
|
||||||
else if (event.prev_content && (event.prev_content.displayname !== event.content.displayname)) {
|
else if (event.prev_content && (event.prev_content.displayname !== event.content.displayname)) {
|
||||||
memberChanges = "displayname";
|
memberChanges = "displayname";
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark the key which changed
|
// mark the key which changed
|
||||||
event.changedKey = memberChanges;
|
event.changedKey = memberChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// modify state before adding the message so it points to the right thing.
|
||||||
|
// The events are copied to avoid referencing the same event when adding
|
||||||
|
// the message (circular json structures)
|
||||||
|
if (isStateEvent || isLiveEvent) {
|
||||||
|
var newEvent = angular.copy(event);
|
||||||
|
newEvent.cnt = event.content;
|
||||||
|
room.current_room_state.storeStateEvent(newEvent);
|
||||||
|
}
|
||||||
|
else if (!isLiveEvent) {
|
||||||
|
// mutate the old room state
|
||||||
|
var oldEvent = angular.copy(event);
|
||||||
|
oldEvent.cnt = event.content;
|
||||||
|
if (event.prev_content) {
|
||||||
|
// the m.room.member event we are handling is the NEW event. When
|
||||||
|
// we keep going back in time, we want the PREVIOUS value for displaying
|
||||||
|
// names/etc, hence the clobber here.
|
||||||
|
oldEvent.cnt = event.prev_content;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.changedKey === "membership" && event.content.membership === "join") {
|
||||||
|
// join has a prev_content but it doesn't contain all the info unlike the join, so use that.
|
||||||
|
oldEvent.cnt = event.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
room.old_room_state.storeStateEvent(oldEvent);
|
||||||
|
}
|
||||||
|
|
||||||
// If there was a change we want to display, dump it in the message
|
// If there was a change we want to display, dump it in the message
|
||||||
// list.
|
// list. This has to be done after room state is updated.
|
||||||
if (memberChanges) {
|
if (memberChanges) {
|
||||||
if (isLiveEvent) {
|
room.addMessageEvent(event, !isLiveEvent);
|
||||||
$rootScope.events.rooms[event.room_id].messages.push(event);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$rootScope.events.rooms[event.room_id].messages.unshift(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use data from state event or the latest data from the stream.
|
|
||||||
// Do not care of events that come when paginating back
|
|
||||||
if (isStateEvent || isLiveEvent) {
|
|
||||||
$rootScope.events.rooms[event.room_id].members[event.state_key] = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
$rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent, isStateEvent);
|
$rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent, isStateEvent);
|
||||||
};
|
};
|
||||||
@ -283,30 +253,28 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
};
|
};
|
||||||
|
|
||||||
var handlePowerLevels = function(event, isLiveEvent) {
|
var handlePowerLevels = function(event, isLiveEvent) {
|
||||||
// Keep the latest data. Do not care of events that come when paginating back
|
handleRoomStateEvent(event, isLiveEvent);
|
||||||
if (!$rootScope.events.rooms[event.room_id][event.type] || isLiveEvent) {
|
|
||||||
$rootScope.events.rooms[event.room_id][event.type] = event;
|
|
||||||
$rootScope.$broadcast(POWERLEVEL_EVENT, event, isLiveEvent);
|
$rootScope.$broadcast(POWERLEVEL_EVENT, event, isLiveEvent);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var handleRoomName = function(event, isLiveEvent, isStateEvent) {
|
var handleRoomName = function(event, isLiveEvent, isStateEvent) {
|
||||||
console.log("handleRoomName room_id: " + event.room_id + " - isLiveEvent: " + isLiveEvent + " - name: " + event.content.name);
|
console.log("handleRoomName room_id: " + event.room_id + " - isLiveEvent: " + isLiveEvent + " - name: " + event.content.name);
|
||||||
handleRoomDateEvent(event, isLiveEvent, !isStateEvent);
|
handleRoomStateEvent(event, isLiveEvent, !isStateEvent);
|
||||||
$rootScope.$broadcast(NAME_EVENT, event, isLiveEvent);
|
$rootScope.$broadcast(NAME_EVENT, event, isLiveEvent);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var handleRoomTopic = function(event, isLiveEvent, isStateEvent) {
|
var handleRoomTopic = function(event, isLiveEvent, isStateEvent) {
|
||||||
console.log("handleRoomTopic room_id: " + event.room_id + " - isLiveEvent: " + isLiveEvent + " - topic: " + event.content.topic);
|
console.log("handleRoomTopic room_id: " + event.room_id + " - isLiveEvent: " + isLiveEvent + " - topic: " + event.content.topic);
|
||||||
handleRoomDateEvent(event, isLiveEvent, !isStateEvent);
|
handleRoomStateEvent(event, isLiveEvent, !isStateEvent);
|
||||||
$rootScope.$broadcast(TOPIC_EVENT, event, isLiveEvent);
|
$rootScope.$broadcast(TOPIC_EVENT, event, isLiveEvent);
|
||||||
};
|
};
|
||||||
|
|
||||||
var handleCallEvent = function(event, isLiveEvent) {
|
var handleCallEvent = function(event, isLiveEvent) {
|
||||||
$rootScope.$broadcast(CALL_EVENT, event, isLiveEvent);
|
$rootScope.$broadcast(CALL_EVENT, event, isLiveEvent);
|
||||||
if (event.type === 'm.call.invite') {
|
if (event.type === 'm.call.invite') {
|
||||||
$rootScope.events.rooms[event.room_id].messages.push(event);
|
var room = modelService.getRoom(event.room_id);
|
||||||
|
room.addMessageEvent(event, !isLiveEvent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -320,8 +288,9 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
// we need to remove something possibly: do we know the redacted
|
// we need to remove something possibly: do we know the redacted
|
||||||
// event ID?
|
// event ID?
|
||||||
if (eventMap[event.redacts]) {
|
if (eventMap[event.redacts]) {
|
||||||
|
var room = modelService.getRoom(event.room_id);
|
||||||
// remove event from list of messages in this room.
|
// remove event from list of messages in this room.
|
||||||
var eventList = $rootScope.events.rooms[event.room_id].messages;
|
var eventList = room.events;
|
||||||
for (var i=0; i<eventList.length; i++) {
|
for (var i=0; i<eventList.length; i++) {
|
||||||
if (eventList[i].event_id === event.redacts) {
|
if (eventList[i].event_id === event.redacts) {
|
||||||
console.log("Removing event " + event.redacts);
|
console.log("Removing event " + event.redacts);
|
||||||
@ -330,51 +299,10 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// broadcast the redaction so controllers can nuke this
|
|
||||||
console.log("Redacted an event.");
|
console.log("Redacted an event.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the index of the event in $rootScope.events.rooms[room_id].messages
|
|
||||||
* @param {type} room_id the room id
|
|
||||||
* @param {type} event_id the event id to look for
|
|
||||||
* @returns {Number | undefined} the index. undefined if not found.
|
|
||||||
*/
|
|
||||||
var getRoomEventIndex = function(room_id, event_id) {
|
|
||||||
var index;
|
|
||||||
|
|
||||||
var room = $rootScope.events.rooms[room_id];
|
|
||||||
if (room) {
|
|
||||||
// Start looking from the tail since the first goal of this function
|
|
||||||
// is to find a messaged among the latest ones
|
|
||||||
for (var i = room.messages.length - 1; i > 0; i--) {
|
|
||||||
var message = room.messages[i];
|
|
||||||
if (event_id === message.event_id) {
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the member object of a room member
|
|
||||||
* @param {String} room_id the room id
|
|
||||||
* @param {String} user_id the id of the user
|
|
||||||
* @returns {undefined | Object} the member object of this user in this room if he is part of the room
|
|
||||||
*/
|
|
||||||
var getMember = function(room_id, user_id) {
|
|
||||||
var member;
|
|
||||||
|
|
||||||
var room = $rootScope.events.rooms[room_id];
|
|
||||||
if (room) {
|
|
||||||
member = room.members[user_id];
|
|
||||||
}
|
|
||||||
return member;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the display name of an user acccording to data already downloaded
|
* Return the display name of an user acccording to data already downloaded
|
||||||
* @param {String} room_id the room id
|
* @param {String} room_id the room id
|
||||||
@ -385,17 +313,17 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
var displayName;
|
var displayName;
|
||||||
|
|
||||||
// Get the user display name from the member list of the room
|
// Get the user display name from the member list of the room
|
||||||
var member = getMember(room_id, user_id);
|
var member = modelService.getMember(room_id, user_id);
|
||||||
if (member && member.content.displayname) { // Do not consider null displayname
|
if (member && member.content.displayname) { // Do not consider null displayname
|
||||||
displayName = member.content.displayname;
|
displayName = member.content.displayname;
|
||||||
|
|
||||||
// Disambiguate users who have the same displayname in the room
|
// Disambiguate users who have the same displayname in the room
|
||||||
if (user_id !== matrixService.config().user_id) {
|
if (user_id !== matrixService.config().user_id) {
|
||||||
var room = $rootScope.events.rooms[room_id];
|
var room = modelService.getRoom(room_id);
|
||||||
|
|
||||||
for (var member_id in room.members) {
|
for (var member_id in room.current_room_state.members) {
|
||||||
if (room.members.hasOwnProperty(member_id) && member_id !== user_id) {
|
if (room.current_room_state.members.hasOwnProperty(member_id) && member_id !== user_id) {
|
||||||
var member2 = room.members[member_id];
|
var member2 = room.current_room_state.members[member_id];
|
||||||
if (member2.content.displayname && member2.content.displayname === displayName) {
|
if (member2.content.displayname && member2.content.displayname === displayName) {
|
||||||
displayName = displayName + " (" + user_id + ")";
|
displayName = displayName + " (" + user_id + ")";
|
||||||
break;
|
break;
|
||||||
@ -434,18 +362,8 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
$rootScope.$broadcast(RESET_EVENT);
|
$rootScope.$broadcast(RESET_EVENT);
|
||||||
},
|
},
|
||||||
|
|
||||||
initRoom: function(room) {
|
|
||||||
initRoom(room.room_id, room);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleEvent: function(event, isLiveEvent, isStateEvent) {
|
handleEvent: function(event, isLiveEvent, isStateEvent) {
|
||||||
|
|
||||||
// FIXME: /initialSync on a particular room is not yet available
|
|
||||||
// So initRoom on a new room is not called. Make sure the room data is initialised here
|
|
||||||
if (event.room_id) {
|
|
||||||
initRoom(event.room_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid duplicated events
|
// Avoid duplicated events
|
||||||
// Needed for rooms where initialSync has not been done.
|
// Needed for rooms where initialSync has not been done.
|
||||||
// In this case, we do not know where to start pagination. So, it starts from the END
|
// In this case, we do not know where to start pagination. So, it starts from the END
|
||||||
@ -504,11 +422,11 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
// displays on the Room Info screen.
|
// displays on the Room Info screen.
|
||||||
if (typeof(event.state_key) === "string") { // incls. 0-len strings
|
if (typeof(event.state_key) === "string") { // incls. 0-len strings
|
||||||
if (event.room_id) {
|
if (event.room_id) {
|
||||||
handleRoomDateEvent(event, isLiveEvent, false);
|
handleRoomStateEvent(event, isLiveEvent, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log("Unable to handle event type " + event.type);
|
console.log("Unable to handle event type " + event.type);
|
||||||
console.log(JSON.stringify(event, undefined, 4));
|
// console.log(JSON.stringify(event, undefined, 4));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -524,8 +442,6 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
|
|
||||||
// Handle messages from /initialSync or /messages
|
// Handle messages from /initialSync or /messages
|
||||||
handleRoomMessages: function(room_id, messages, isLiveEvents, dir) {
|
handleRoomMessages: function(room_id, messages, isLiveEvents, dir) {
|
||||||
initRoom(room_id);
|
|
||||||
|
|
||||||
var events = messages.chunk;
|
var events = messages.chunk;
|
||||||
|
|
||||||
// Handles messages according to their time order
|
// Handles messages according to their time order
|
||||||
@ -536,21 +452,67 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store how far back we've paginated
|
// Store how far back we've paginated
|
||||||
$rootScope.events.rooms[room_id].pagination.earliest_token = messages.end;
|
var room = modelService.getRoom(room_id);
|
||||||
|
room.old_room_state.pagination_token = messages.end;
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// InitialSync returns messages in chronological order
|
// InitialSync returns messages in chronological order, so invert
|
||||||
|
// it to get most recent > oldest
|
||||||
for (var i=events.length - 1; i>=0; i--) {
|
for (var i=events.length - 1; i>=0; i--) {
|
||||||
this.handleEvent(events[i], isLiveEvents, isLiveEvents);
|
this.handleEvent(events[i], isLiveEvents, isLiveEvents);
|
||||||
}
|
}
|
||||||
// Store where to start pagination
|
// Store where to start pagination
|
||||||
$rootScope.events.rooms[room_id].pagination.earliest_token = messages.start;
|
var room = modelService.getRoom(room_id);
|
||||||
|
room.old_room_state.pagination_token = messages.start;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleInitialSyncDone: function(initialSyncData) {
|
handleInitialSyncDone: function(response) {
|
||||||
console.log("# handleInitialSyncDone");
|
console.log("# handleInitialSyncDone");
|
||||||
initialSyncDeferred.resolve(initialSyncData);
|
|
||||||
|
var rooms = response.data.rooms;
|
||||||
|
for (var i = 0; i < rooms.length; ++i) {
|
||||||
|
var room = rooms[i];
|
||||||
|
|
||||||
|
// FIXME: This is ming: the HS should be sending down the m.room.member
|
||||||
|
// event for the invite in .state but it isn't, so fudge it for now.
|
||||||
|
if (room.inviter && room.membership === "invite") {
|
||||||
|
var me = matrixService.config().user_id;
|
||||||
|
var fakeEvent = {
|
||||||
|
event_id: "__FAKE__" + room.room_id,
|
||||||
|
user_id: room.inviter,
|
||||||
|
origin_server_ts: 0,
|
||||||
|
room_id: room.room_id,
|
||||||
|
state_key: me,
|
||||||
|
type: "m.room.member",
|
||||||
|
content: {
|
||||||
|
membership: "invite"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!room.state) {
|
||||||
|
room.state = [];
|
||||||
|
}
|
||||||
|
room.state.push(fakeEvent);
|
||||||
|
console.log("RECV /initialSync invite >> "+room.room_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newRoom = modelService.getRoom(room.room_id);
|
||||||
|
newRoom.current_room_state.storeStateEvents(room.state);
|
||||||
|
newRoom.old_room_state.storeStateEvents(room.state);
|
||||||
|
|
||||||
|
// this should be done AFTER storing state events since these
|
||||||
|
// messages may make the old_room_state diverge.
|
||||||
|
if ("messages" in room) {
|
||||||
|
this.handleRoomMessages(room.room_id, room.messages, false);
|
||||||
|
newRoom.current_room_state.pagination_token = room.messages.end;
|
||||||
|
newRoom.old_room_state.pagination_token = room.messages.start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var presence = response.data.presence;
|
||||||
|
this.handleEvents(presence, false);
|
||||||
|
|
||||||
|
initialSyncDeferred.resolve(response);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Returns a promise that resolves when the initialSync request has been processed
|
// Returns a promise that resolves when the initialSync request has been processed
|
||||||
@ -571,17 +533,15 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
getLastMessage: function(room_id, filterEcho) {
|
getLastMessage: function(room_id, filterEcho) {
|
||||||
var lastMessage;
|
var lastMessage;
|
||||||
|
|
||||||
var room = $rootScope.events.rooms[room_id];
|
var events = modelService.getRoom(room_id).events;
|
||||||
if (room) {
|
for (var i = events.length - 1; i >= 0; i--) {
|
||||||
for (var i = room.messages.length - 1; i >= 0; i--) {
|
var message = events[i];
|
||||||
var message = room.messages[i];
|
|
||||||
|
|
||||||
if (!filterEcho || undefined === message.echo_msg_state) {
|
if (!filterEcho || undefined === message.echo_msg_state) {
|
||||||
lastMessage = message;
|
lastMessage = message;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return lastMessage;
|
return lastMessage;
|
||||||
},
|
},
|
||||||
@ -594,32 +554,40 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
getUsersCountInRoom: function(room_id) {
|
getUsersCountInRoom: function(room_id) {
|
||||||
var memberCount;
|
var memberCount;
|
||||||
|
|
||||||
var room = $rootScope.events.rooms[room_id];
|
var room = modelService.getRoom(room_id);
|
||||||
if (room) {
|
|
||||||
memberCount = 0;
|
memberCount = 0;
|
||||||
|
for (var i in room.current_room_state.members) {
|
||||||
|
if (!room.current_room_state.members.hasOwnProperty(i)) continue;
|
||||||
|
|
||||||
for (var i in room.members) {
|
var member = room.current_room_state.members[i];
|
||||||
if (!room.members.hasOwnProperty(i)) continue;
|
|
||||||
|
|
||||||
var member = room.members[i];
|
if ("join" === member.content.membership) {
|
||||||
|
|
||||||
if ("join" === member.membership) {
|
|
||||||
memberCount = memberCount + 1;
|
memberCount = memberCount + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return memberCount;
|
return memberCount;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the member object of a room member
|
* Return the power level of an user in a particular room
|
||||||
* @param {String} room_id the room id
|
* @param {String} room_id the room id
|
||||||
* @param {String} user_id the id of the user
|
* @param {String} user_id the user id
|
||||||
* @returns {undefined | Object} the member object of this user in this room if he is part of the room
|
* @returns {Number} a value between 0 and 10
|
||||||
*/
|
*/
|
||||||
getMember: function(room_id, user_id) {
|
getUserPowerLevel: function(room_id, user_id) {
|
||||||
return getMember(room_id, user_id);
|
var powerLevel = 0;
|
||||||
|
var room = modelService.getRoom(room_id).current_room_state;
|
||||||
|
if (room.state("m.room.power_levels")) {
|
||||||
|
if (user_id in room.state("m.room.power_levels").content) {
|
||||||
|
powerLevel = room.state("m.room.power_levels").content[user_id];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Use the room default user power
|
||||||
|
powerLevel = room.state("m.room.power_levels").content["default"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return powerLevel;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -630,18 +598,6 @@ function(matrixService, $rootScope, $q, $timeout, mPresence, notificationService
|
|||||||
*/
|
*/
|
||||||
getUserDisplayName: function(room_id, user_id) {
|
getUserDisplayName: function(room_id, user_id) {
|
||||||
return getUserDisplayName(room_id, user_id);
|
return getUserDisplayName(room_id, user_id);
|
||||||
},
|
|
||||||
|
|
||||||
setRoomVisibility: function(room_id, visible) {
|
|
||||||
if (!visible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
initRoom(room_id);
|
|
||||||
|
|
||||||
var room = $rootScope.events.rooms[room_id];
|
|
||||||
if (room) {
|
|
||||||
room.visibility = visible;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
@ -109,25 +109,6 @@ angular.module('eventStreamService', [])
|
|||||||
// without requiring to make an additional request
|
// without requiring to make an additional request
|
||||||
matrixService.initialSync(30, false).then(
|
matrixService.initialSync(30, false).then(
|
||||||
function(response) {
|
function(response) {
|
||||||
var rooms = response.data.rooms;
|
|
||||||
for (var i = 0; i < rooms.length; ++i) {
|
|
||||||
var room = rooms[i];
|
|
||||||
|
|
||||||
eventHandlerService.initRoom(room);
|
|
||||||
|
|
||||||
if ("messages" in room) {
|
|
||||||
eventHandlerService.handleRoomMessages(room.room_id, room.messages, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("state" in room) {
|
|
||||||
eventHandlerService.handleEvents(room.state, false, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var presence = response.data.presence;
|
|
||||||
eventHandlerService.handleEvents(presence, false);
|
|
||||||
|
|
||||||
// Initial sync is done
|
|
||||||
eventHandlerService.handleInitialSyncDone(response);
|
eventHandlerService.handleInitialSyncDone(response);
|
||||||
|
|
||||||
// Start event streaming from that point
|
// Start event streaming from that point
|
||||||
|
@ -46,7 +46,7 @@ var isWebRTCSupported = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
angular.module('MatrixCall', [])
|
angular.module('MatrixCall', [])
|
||||||
.factory('MatrixCall', ['matrixService', 'matrixPhoneService', '$rootScope', '$timeout', function MatrixCallFactory(matrixService, matrixPhoneService, $rootScope, $timeout) {
|
.factory('MatrixCall', ['matrixService', 'matrixPhoneService', 'modelService', '$rootScope', '$timeout', function MatrixCallFactory(matrixService, matrixPhoneService, modelService, $rootScope, $timeout) {
|
||||||
$rootScope.isWebRTCSupported = isWebRTCSupported();
|
$rootScope.isWebRTCSupported = isWebRTCSupported();
|
||||||
|
|
||||||
var MatrixCall = function(room_id) {
|
var MatrixCall = function(room_id) {
|
||||||
@ -213,7 +213,7 @@ angular.module('MatrixCall', [])
|
|||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var roomMembers = $rootScope.events.rooms[this.room_id].members;
|
var roomMembers = modelService.getRoom(this.room_id).current_room_state.members;
|
||||||
if (roomMembers[matrixService.config().user_id].membership != 'join') {
|
if (roomMembers[matrixService.config().user_id].membership != 'join') {
|
||||||
console.log("We need to join the room before we can accept this call");
|
console.log("We need to join the room before we can accept this call");
|
||||||
matrixService.join(this.room_id).then(function() {
|
matrixService.join(this.room_id).then(function() {
|
||||||
|
@ -19,23 +19,24 @@
|
|||||||
angular.module('matrixFilter', [])
|
angular.module('matrixFilter', [])
|
||||||
|
|
||||||
// Compute the room name according to information we have
|
// Compute the room name according to information we have
|
||||||
.filter('mRoomName', ['$rootScope', 'matrixService', 'eventHandlerService', function($rootScope, matrixService, eventHandlerService) {
|
// TODO: It would be nice if this was stateless and had no dependencies. That would
|
||||||
|
// make the business logic here a lot easier to see.
|
||||||
|
.filter('mRoomName', ['$rootScope', 'matrixService', 'eventHandlerService', 'modelService',
|
||||||
|
function($rootScope, matrixService, eventHandlerService, modelService) {
|
||||||
return function(room_id) {
|
return function(room_id) {
|
||||||
var roomName;
|
var roomName;
|
||||||
|
|
||||||
// If there is an alias, use it
|
// If there is an alias, use it
|
||||||
// TODO: only one alias is managed for now
|
// TODO: only one alias is managed for now
|
||||||
var alias = matrixService.getRoomIdToAliasMapping(room_id);
|
var alias = matrixService.getRoomIdToAliasMapping(room_id);
|
||||||
|
var room = modelService.getRoom(room_id).current_room_state;
|
||||||
|
|
||||||
var room = $rootScope.events.rooms[room_id];
|
var room_name_event = room.state("m.room.name");
|
||||||
if (room) {
|
|
||||||
// Get name from room state date
|
|
||||||
var room_name_event = room["m.room.name"];
|
|
||||||
|
|
||||||
// Determine if it is a public room
|
// Determine if it is a public room
|
||||||
var isPublicRoom = false;
|
var isPublicRoom = false;
|
||||||
if (room["m.room.join_rules"] && room["m.room.join_rules"].content) {
|
if (room.state("m.room.join_rules") && room.state("m.room.join_rules").content) {
|
||||||
isPublicRoom = ("public" === room["m.room.join_rules"].content.join_rule);
|
isPublicRoom = ("public" === room.state("m.room.join_rules").content.join_rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (room_name_event) {
|
if (room_name_event) {
|
||||||
@ -44,12 +45,11 @@ angular.module('matrixFilter', [])
|
|||||||
else if (alias) {
|
else if (alias) {
|
||||||
roomName = alias;
|
roomName = alias;
|
||||||
}
|
}
|
||||||
else if (room.members && !isPublicRoom) { // Do not rename public room
|
else if (Object.keys(room.members).length > 0 && !isPublicRoom) { // Do not rename public room
|
||||||
|
|
||||||
var user_id = matrixService.config().user_id;
|
var user_id = matrixService.config().user_id;
|
||||||
// Else, build the name from its users
|
|
||||||
// Limit the room renaming to 1:1 room
|
// this is a "one to one" room and should have the name of the other user.
|
||||||
if (2 === Object.keys(room.members).length) {
|
if (Object.keys(room.members).length === 2) {
|
||||||
for (var i in room.members) {
|
for (var i in room.members) {
|
||||||
if (!room.members.hasOwnProperty(i)) continue;
|
if (!room.members.hasOwnProperty(i)) continue;
|
||||||
|
|
||||||
@ -60,61 +60,31 @@ angular.module('matrixFilter', [])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Object.keys(room.members).length <= 1) {
|
else if (Object.keys(room.members).length === 1) {
|
||||||
|
// this could be just us (self-chat) or could be the other person
|
||||||
var otherUserId;
|
// in a room if they have invited us to the room. Find out which.
|
||||||
|
var otherUserId = Object.keys(room.members)[0];
|
||||||
if (Object.keys(room.members)[0]) {
|
if (otherUserId === user_id) {
|
||||||
otherUserId = Object.keys(room.members)[0];
|
// it's us, we may have been invited to this room or it could
|
||||||
// this could be an invite event (from event stream)
|
// be a self chat.
|
||||||
if (otherUserId === user_id &&
|
if (room.members[otherUserId].content.membership === "invite") {
|
||||||
room.members[user_id].content.membership === "invite") {
|
// someone invited us, use the right ID.
|
||||||
// this is us being invited to this room, so the
|
roomName = eventHandlerService.getUserDisplayName(room_id, room.members[otherUserId].user_id);
|
||||||
// *user_id* is the other user ID and not the state
|
|
||||||
// key.
|
|
||||||
otherUserId = room.members[user_id].user_id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// it's got to be an invite, or failing that a self-chat;
|
|
||||||
otherUserId = room.inviter || user_id;
|
|
||||||
/*
|
|
||||||
// XXX: This should all be unnecessary now thanks to using the /rooms/<room>/roomid API
|
|
||||||
|
|
||||||
// The other member may be in the invite list, get all invited users
|
|
||||||
var invitedUserIDs = [];
|
|
||||||
|
|
||||||
// XXX: *SURELY* we shouldn't have to trawl through the whole messages list to
|
|
||||||
// find invite - surely the other user should be in room.members with state invited? :/ --Matthew
|
|
||||||
for (var i in room.messages) {
|
|
||||||
var message = room.messages[i];
|
|
||||||
if ("m.room.member" === message.type && "invite" === message.content.membership) {
|
|
||||||
// Filter out the current user
|
|
||||||
var member_id = message.state_key;
|
|
||||||
if (member_id === user_id) {
|
|
||||||
member_id = message.user_id;
|
|
||||||
}
|
|
||||||
if (member_id !== user_id) {
|
|
||||||
// Make sure there is no duplicate user
|
|
||||||
if (-1 === invitedUserIDs.indexOf(member_id)) {
|
|
||||||
invitedUserIDs.push(member_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now, only 1:1 room needs to be renamed. It means only 1 invited user
|
|
||||||
if (1 === invitedUserIDs.length) {
|
|
||||||
otherUserId = invitedUserIDs[0];
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the user display name
|
|
||||||
roomName = eventHandlerService.getUserDisplayName(room_id, otherUserId);
|
roomName = eventHandlerService.getUserDisplayName(room_id, otherUserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else { // it isn't us, so use their name if we know it.
|
||||||
|
roomName = eventHandlerService.getUserDisplayName(room_id, otherUserId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (Object.keys(room.members).length === 0) {
|
||||||
|
// this shouldn't be possible
|
||||||
|
console.error("0 members in room >> " + room_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Always show the alias in the room displayed name
|
// Always show the alias in the room displayed name
|
||||||
if (roomName && alias && alias !== roomName) {
|
if (roomName && alias && alias !== roomName) {
|
||||||
@ -124,14 +94,6 @@ angular.module('matrixFilter', [])
|
|||||||
if (undefined === roomName) {
|
if (undefined === roomName) {
|
||||||
// By default, use the room ID
|
// By default, use the room ID
|
||||||
roomName = room_id;
|
roomName = room_id;
|
||||||
|
|
||||||
// XXX: this is *INCREDIBLY* heavy logging for a function that calls every single
|
|
||||||
// time any kind of digest runs which refreshes a room name...
|
|
||||||
// commenting it out for now.
|
|
||||||
|
|
||||||
// Log some information that lead to this leak
|
|
||||||
// console.log("Room ID leak for " + room_id);
|
|
||||||
// console.log("room object: " + JSON.stringify(room, undefined, 4));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return roomName;
|
return roomName;
|
||||||
|
@ -726,56 +726,29 @@ angular.module('matrixService', [])
|
|||||||
return roomId;
|
return roomId;
|
||||||
},
|
},
|
||||||
|
|
||||||
/****** Power levels management ******/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the power level of an user in a particular room
|
|
||||||
* @param {String} room_id the room id
|
|
||||||
* @param {String} user_id the user id
|
|
||||||
* @returns {Number} a value between 0 and 10
|
|
||||||
*/
|
|
||||||
getUserPowerLevel: function(room_id, user_id) {
|
|
||||||
var powerLevel = 0;
|
|
||||||
var room = $rootScope.events.rooms[room_id];
|
|
||||||
if (room && room["m.room.power_levels"]) {
|
|
||||||
if (user_id in room["m.room.power_levels"].content) {
|
|
||||||
powerLevel = room["m.room.power_levels"].content[user_id];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Use the room default user power
|
|
||||||
powerLevel = room["m.room.power_levels"].content["default"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return powerLevel;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change or reset the power level of a user
|
* Change or reset the power level of a user
|
||||||
* @param {String} room_id the room id
|
* @param {String} room_id the room id
|
||||||
* @param {String} user_id the user id
|
* @param {String} user_id the user id
|
||||||
* @param {Number} powerLevel a value between 0 and 10
|
* @param {Number} powerLevel The desired power level.
|
||||||
* If undefined, the user power level will be reset, ie he will use the default room user power level
|
* If undefined, the user power level will be reset, ie he will use the default room user power level
|
||||||
|
* @param event The existing m.room.power_levels event if one exists.
|
||||||
* @returns {promise} an $http promise
|
* @returns {promise} an $http promise
|
||||||
*/
|
*/
|
||||||
setUserPowerLevel: function(room_id, user_id, powerLevel) {
|
setUserPowerLevel: function(room_id, user_id, powerLevel, event) {
|
||||||
|
var content = {};
|
||||||
// Hack: currently, there is no home server API so do it by hand by updating
|
if (event) {
|
||||||
// the current m.room.power_levels of the room and send it to the server
|
// if there is an existing event, copy the content as it contains
|
||||||
var room = $rootScope.events.rooms[room_id];
|
// the power level values for other members which we do not want
|
||||||
if (room && room["m.room.power_levels"]) {
|
// to modify.
|
||||||
var content = angular.copy(room["m.room.power_levels"].content);
|
content = angular.copy(event.content);
|
||||||
|
}
|
||||||
content[user_id] = powerLevel;
|
content[user_id] = powerLevel;
|
||||||
|
|
||||||
var path = "/rooms/$room_id/state/m.room.power_levels";
|
var path = "/rooms/$room_id/state/m.room.power_levels";
|
||||||
path = path.replace("$room_id", encodeURIComponent(room_id));
|
path = path.replace("$room_id", encodeURIComponent(room_id));
|
||||||
|
|
||||||
return doRequest("PUT", path, undefined, content);
|
return doRequest("PUT", path, undefined, content);
|
||||||
}
|
|
||||||
|
|
||||||
// The room does not exist or does not contain power_levels data
|
|
||||||
var deferred = $q.defer();
|
|
||||||
deferred.reject({data:{error: "Invalid room: " + room_id}});
|
|
||||||
return deferred.promise;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getTurnServer: function() {
|
getTurnServer: function() {
|
||||||
|
170
webclient/components/matrix/model-service.js
Normal file
170
webclient/components/matrix/model-service.js
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 OpenMarket Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/*
|
||||||
|
This service serves as the entry point for all models in the app. If access to
|
||||||
|
underlying data in a room is required, then this service should be used as the
|
||||||
|
dependency.
|
||||||
|
*/
|
||||||
|
// NB: This is more explicit than linking top-level models to $rootScope
|
||||||
|
// in that by adding this service as a dep you are clearly saying "this X
|
||||||
|
// needs access to the underlying data store", rather than polluting the
|
||||||
|
// $rootScope.
|
||||||
|
angular.module('modelService', [])
|
||||||
|
.factory('modelService', ['matrixService', function(matrixService) {
|
||||||
|
|
||||||
|
/***** Room Object *****/
|
||||||
|
var Room = function Room(room_id) {
|
||||||
|
this.room_id = room_id;
|
||||||
|
this.old_room_state = new RoomState();
|
||||||
|
this.current_room_state = new RoomState();
|
||||||
|
this.events = []; // events which can be displayed on the UI. TODO move?
|
||||||
|
};
|
||||||
|
Room.prototype = {
|
||||||
|
addMessageEvents: function addMessageEvents(events, toFront) {
|
||||||
|
for (var i=0; i<events.length; i++) {
|
||||||
|
this.addMessageEvent(events[i], toFront);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addMessageEvent: function addMessageEvent(event, toFront) {
|
||||||
|
// every message must reference the RoomMember which made it *at
|
||||||
|
// that time* so things like display names display correctly.
|
||||||
|
var stateAtTheTime = toFront ? this.old_room_state : this.current_room_state;
|
||||||
|
event.__room_member = stateAtTheTime.getStateEvent("m.room.member", event.user_id);
|
||||||
|
if (event.type === "m.room.member" && event.content.membership === "invite") {
|
||||||
|
// give information on both the inviter and invitee
|
||||||
|
event.__target_room_member = stateAtTheTime.getStateEvent("m.room.member", event.state_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toFront) {
|
||||||
|
this.events.unshift(event);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.events.push(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addOrReplaceMessageEvent: function addOrReplaceMessageEvent(event, toFront) {
|
||||||
|
// Start looking from the tail since the first goal of this function
|
||||||
|
// is to find a message among the latest ones
|
||||||
|
for (var i = this.events.length - 1; i >= 0; i--) {
|
||||||
|
var storedEvent = this.events[i];
|
||||||
|
if (storedEvent.event_id === event.event_id) {
|
||||||
|
// It's clobbering time!
|
||||||
|
this.events[i] = event;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.addMessageEvent(event, toFront);
|
||||||
|
},
|
||||||
|
|
||||||
|
leave: function leave() {
|
||||||
|
return matrixService.leave(this.room_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/***** Room State Object *****/
|
||||||
|
var RoomState = function RoomState() {
|
||||||
|
// list of RoomMember
|
||||||
|
this.members = {};
|
||||||
|
// state events, the key is a compound of event type + state_key
|
||||||
|
this.state_events = {};
|
||||||
|
this.pagination_token = "";
|
||||||
|
};
|
||||||
|
RoomState.prototype = {
|
||||||
|
// get a state event for this room from this.state_events. State events
|
||||||
|
// are unique per type+state_key tuple, with a lot of events using 0-len
|
||||||
|
// state keys. To make it not Really Annoying to access, this method is
|
||||||
|
// provided which can just be given the type and it will return the
|
||||||
|
// 0-len event by default.
|
||||||
|
state: function state(type, state_key) {
|
||||||
|
if (!type) {
|
||||||
|
return undefined; // event type MUST be specified
|
||||||
|
}
|
||||||
|
if (!state_key) {
|
||||||
|
return this.state_events[type]; // treat as 0-len state key
|
||||||
|
}
|
||||||
|
return this.state_events[type + state_key];
|
||||||
|
},
|
||||||
|
|
||||||
|
storeStateEvent: function storeState(event) {
|
||||||
|
this.state_events[event.type + event.state_key] = event;
|
||||||
|
if (event.type === "m.room.member") {
|
||||||
|
this.members[event.state_key] = event;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
storeStateEvents: function storeState(events) {
|
||||||
|
if (!events) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (var i=0; i<events.length; i++) {
|
||||||
|
this.storeStateEvent(events[i]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getStateEvent: function getStateEvent(event_type, state_key) {
|
||||||
|
return this.state_events[event_type + state_key];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/***** Room Member Object *****/
|
||||||
|
var RoomMember = function RoomMember() {
|
||||||
|
this.event = {}; // the m.room.member event representing the RoomMember.
|
||||||
|
this.user = undefined; // the User
|
||||||
|
};
|
||||||
|
|
||||||
|
/***** User Object *****/
|
||||||
|
var User = function User() {
|
||||||
|
this.event = {}; // the m.presence event representing the User.
|
||||||
|
};
|
||||||
|
|
||||||
|
// rooms are stored here when they come in.
|
||||||
|
var rooms = {
|
||||||
|
// roomid: <Room>
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("Models inited.");
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
getRoom: function(roomId) {
|
||||||
|
if(!rooms[roomId]) {
|
||||||
|
rooms[roomId] = new Room(roomId);
|
||||||
|
}
|
||||||
|
return rooms[roomId];
|
||||||
|
},
|
||||||
|
|
||||||
|
getRooms: function() {
|
||||||
|
return rooms;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the member object of a room member
|
||||||
|
* @param {String} room_id the room id
|
||||||
|
* @param {String} user_id the id of the user
|
||||||
|
* @returns {undefined | Object} the member object of this user in this room if he is part of the room
|
||||||
|
*/
|
||||||
|
getMember: function(room_id, user_id) {
|
||||||
|
var room = this.getRoom(room_id);
|
||||||
|
return room.current_room_state.members[user_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}]);
|
@ -58,7 +58,6 @@ angular.module('HomeController', ['matrixService', 'eventHandlerService', 'Recen
|
|||||||
// Add room_alias & room_display_name members
|
// Add room_alias & room_display_name members
|
||||||
angular.extend(room, matrixService.getRoomAliasAndDisplayName(room));
|
angular.extend(room, matrixService.getRoomAliasAndDisplayName(room));
|
||||||
|
|
||||||
eventHandlerService.setRoomVisibility(room.room_id, "public");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
<script type='text/javascript' src='js/jquery-1.8.3.min.js'></script>
|
<script type='text/javascript' src='js/jquery-1.8.3.min.js'></script>
|
||||||
<script type="text/javascript" src="https://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>
|
<script type="text/javascript" src="https://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>
|
||||||
<script src="js/angular.min.js"></script>
|
<script src="js/angular.js"></script>
|
||||||
<script src="js/angular-route.min.js"></script>
|
<script src="js/angular-route.min.js"></script>
|
||||||
<script src="js/angular-sanitize.min.js"></script>
|
<script src="js/angular-sanitize.min.js"></script>
|
||||||
<script src="js/angular-animate.min.js"></script>
|
<script src="js/angular-animate.min.js"></script>
|
||||||
@ -42,6 +42,7 @@
|
|||||||
<script src="components/matrix/event-stream-service.js"></script>
|
<script src="components/matrix/event-stream-service.js"></script>
|
||||||
<script src="components/matrix/event-handler-service.js"></script>
|
<script src="components/matrix/event-handler-service.js"></script>
|
||||||
<script src="components/matrix/notification-service.js"></script>
|
<script src="components/matrix/notification-service.js"></script>
|
||||||
|
<script src="components/matrix/model-service.js"></script>
|
||||||
<script src="components/matrix/presence-service.js"></script>
|
<script src="components/matrix/presence-service.js"></script>
|
||||||
<script src="components/fileInput/file-input-directive.js"></script>
|
<script src="components/fileInput/file-input-directive.js"></script>
|
||||||
<script src="components/fileUpload/file-upload-service.js"></script>
|
<script src="components/fileUpload/file-upload-service.js"></script>
|
||||||
|
@ -17,12 +17,15 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('RecentsController', ['matrixService', 'matrixFilter'])
|
angular.module('RecentsController', ['matrixService', 'matrixFilter'])
|
||||||
.controller('RecentsController', ['$rootScope', '$scope', 'eventHandlerService',
|
.controller('RecentsController', ['$rootScope', '$scope', 'eventHandlerService', 'modelService',
|
||||||
function($rootScope, $scope, eventHandlerService) {
|
function($rootScope, $scope, eventHandlerService, modelService) {
|
||||||
|
|
||||||
// Expose the service to the view
|
// Expose the service to the view
|
||||||
$scope.eventHandlerService = eventHandlerService;
|
$scope.eventHandlerService = eventHandlerService;
|
||||||
|
|
||||||
|
// retrieve all rooms and expose them
|
||||||
|
$scope.rooms = modelService.getRooms();
|
||||||
|
|
||||||
// $rootScope of the parent where the recents component is included can override this value
|
// $rootScope of the parent where the recents component is included can override this value
|
||||||
// in order to highlight a specific room in the list
|
// in order to highlight a specific room in the list
|
||||||
$rootScope.recentsSelectedRoomID;
|
$rootScope.recentsSelectedRoomID;
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('RecentsController')
|
angular.module('RecentsController')
|
||||||
.filter('orderRecents', ["matrixService", "eventHandlerService", function(matrixService, eventHandlerService) {
|
.filter('orderRecents', ["matrixService", "eventHandlerService", "modelService", function(matrixService, eventHandlerService, modelService) {
|
||||||
return function(rooms) {
|
return function(rooms) {
|
||||||
var user_id = matrixService.config().user_id;
|
var user_id = matrixService.config().user_id;
|
||||||
|
|
||||||
@ -25,26 +25,30 @@ angular.module('RecentsController')
|
|||||||
// The key, room_id, is already in value objects
|
// The key, room_id, is already in value objects
|
||||||
var filtered = [];
|
var filtered = [];
|
||||||
angular.forEach(rooms, function(room, room_id) {
|
angular.forEach(rooms, function(room, room_id) {
|
||||||
|
room.recent = {};
|
||||||
|
var meEvent = room.current_room_state.state("m.room.member", user_id);
|
||||||
// Show the room only if the user has joined it or has been invited
|
// Show the room only if the user has joined it or has been invited
|
||||||
// (ie, do not show it if he has been banned)
|
// (ie, do not show it if he has been banned)
|
||||||
var member = eventHandlerService.getMember(room_id, user_id);
|
var member = modelService.getMember(room_id, user_id);
|
||||||
if (member && ("invite" === member.membership || "join" === member.membership)) {
|
room.recent.me = member;
|
||||||
|
if (member && ("invite" === member.content.membership || "join" === member.content.membership)) {
|
||||||
|
if ("invite" === member.content.membership) {
|
||||||
|
room.recent.inviter = member.user_id;
|
||||||
|
}
|
||||||
// Count users here
|
// Count users here
|
||||||
// TODO: Compute it directly in eventHandlerService
|
// TODO: Compute it directly in eventHandlerService
|
||||||
room.numUsersInRoom = eventHandlerService.getUsersCountInRoom(room_id);
|
room.recent.numUsersInRoom = eventHandlerService.getUsersCountInRoom(room_id);
|
||||||
|
|
||||||
filtered.push(room);
|
filtered.push(room);
|
||||||
}
|
}
|
||||||
else if ("invite" === room.membership) {
|
else if (meEvent && "invite" === meEvent.content.membership) {
|
||||||
// The only information we have about the room is that the user has been invited
|
// The only information we have about the room is that the user has been invited
|
||||||
filtered.push(room);
|
filtered.push(room);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// And time sort them
|
// And time sort them
|
||||||
// The room with the lastest message at first
|
// The room with the latest message at first
|
||||||
filtered.sort(function (roomA, roomB) {
|
filtered.sort(function (roomA, roomB) {
|
||||||
|
|
||||||
var lastMsgRoomA = eventHandlerService.getLastMessage(roomA.room_id, true);
|
var lastMsgRoomA = eventHandlerService.getLastMessage(roomA.room_id, true);
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
<div ng-controller="RecentsController">
|
<div ng-controller="RecentsController">
|
||||||
<table class="recentsTable">
|
<table class="recentsTable">
|
||||||
<tbody ng-repeat="(index, room) in events.rooms | orderRecents"
|
<tbody ng-repeat="(index, room) in rooms | orderRecents"
|
||||||
ng-click="goToPage('room/' + (room.room_alias ? room.room_alias : room.room_id) )"
|
ng-click="goToPage('room/' + (room.room_alias ? room.room_alias : room.room_id) )"
|
||||||
class="recentsRoom"
|
class="recentsRoom"
|
||||||
ng-class="{'recentsRoomSelected': (room.room_id === recentsSelectedRoomID)}">
|
ng-class="{'recentsRoomSelected': (room.room_id === recentsSelectedRoomID)}">
|
||||||
<tr>
|
<tr>
|
||||||
<td ng-class="room['m.room.join_rules'].content.join_rule == 'public' ? 'recentsRoomName recentsPublicRoom' : 'recentsRoomName'">
|
<td ng-class="room.current_room_state.state('m.room.join_rules').content.join_rule == 'public' ? 'recentsRoomName recentsPublicRoom' : 'recentsRoomName'">
|
||||||
{{ room.room_id | mRoomName }}
|
{{ room.room_id | mRoomName }}
|
||||||
</td>
|
</td>
|
||||||
<td class="recentsRoomSummaryUsersCount">
|
<td class="recentsRoomSummaryUsersCount">
|
||||||
<span ng-show="undefined !== room.numUsersInRoom">
|
<span ng-show="undefined !== room.recent.numUsersInRoom">
|
||||||
{{ room.numUsersInRoom || '1' }} {{ room.numUsersInRoom == 1 ? 'user' : 'users' }}
|
{{ room.recent.numUsersInRoom || '1' }} {{ room.recent.numUsersInRoom == 1 ? 'user' : 'users' }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="recentsRoomSummaryTS">
|
<td class="recentsRoomSummaryTS">
|
||||||
@ -27,11 +27,11 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td colspan="3" class="recentsRoomSummary">
|
<td colspan="3" class="recentsRoomSummary">
|
||||||
|
|
||||||
<div ng-show="room.membership === 'invite'">
|
<div ng-show="room.recent.me.content.membership === 'invite'">
|
||||||
{{ room.inviter | mUserDisplayName: room.room_id }} invited you
|
{{ room.recent.inviter | mUserDisplayName: room.room_id }} invited you
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-hide="room.membership === 'invite'" ng-switch="lastMsg.type">
|
<div ng-hide="room.recent.me.membership === 'invite'" ng-switch="lastMsg.type">
|
||||||
<div ng-switch-when="m.room.member">
|
<div ng-switch-when="m.room.member">
|
||||||
<span ng-switch="lastMsg.changedKey">
|
<span ng-switch="lastMsg.changedKey">
|
||||||
<span ng-switch-when="membership">
|
<span ng-switch-when="membership">
|
||||||
|
@ -15,8 +15,8 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
||||||
.controller('RoomController', ['$modal', '$filter', '$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'mPresence', 'eventHandlerService', 'mFileUpload', 'matrixPhoneService', 'MatrixCall', 'notificationService',
|
.controller('RoomController', ['$modal', '$filter', '$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'mPresence', 'eventHandlerService', 'mFileUpload', 'matrixPhoneService', 'MatrixCall', 'notificationService', 'modelService',
|
||||||
function($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, modelService) {
|
||||||
'use strict';
|
'use strict';
|
||||||
var MESSAGES_PER_PAGINATION = 30;
|
var MESSAGES_PER_PAGINATION = 30;
|
||||||
var THUMBNAIL_SIZE = 320;
|
var THUMBNAIL_SIZE = 320;
|
||||||
@ -64,7 +64,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
return;
|
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) {
|
if (nameEvent) {
|
||||||
$scope.name.newNameText = nameEvent.content.name;
|
$scope.name.newNameText = nameEvent.content.name;
|
||||||
}
|
}
|
||||||
@ -105,7 +105,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
console.log("Warning: Already editing topic.");
|
console.log("Warning: Already editing topic.");
|
||||||
return;
|
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) {
|
if (topicEvent) {
|
||||||
$scope.topic.newTopicText = topicEvent.content.topic;
|
$scope.topic.newTopicText = topicEvent.content.topic;
|
||||||
}
|
}
|
||||||
@ -254,11 +254,11 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
$scope.state.paginating = true;
|
$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];
|
var originalTopRow = $("#messageTable>tbody>tr:first")[0];
|
||||||
|
|
||||||
// Paginate events from the point in cache
|
// 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) {
|
function(response) {
|
||||||
|
|
||||||
eventHandlerService.handleRoomMessages($scope.room_id, response.data, false, 'b');
|
eventHandlerService.handleRoomMessages($scope.room_id, response.data, false, 'b');
|
||||||
@ -404,7 +404,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
var updateUserPowerLevel = function(user_id) {
|
var updateUserPowerLevel = function(user_id) {
|
||||||
var member = $scope.members[user_id];
|
var member = $scope.members[user_id];
|
||||||
if (member) {
|
if (member) {
|
||||||
member.powerLevel = matrixService.getUserPowerLevel($scope.room_id, user_id);
|
member.powerLevel = eventHandlerService.getUserPowerLevel($scope.room_id, user_id);
|
||||||
|
|
||||||
normaliseMembersPowerLevels();
|
normaliseMembersPowerLevels();
|
||||||
}
|
}
|
||||||
@ -492,7 +492,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
|
|
||||||
var room_id = matrixService.getAliasToRoomIdMapping(room_alias);
|
var room_id = matrixService.getAliasToRoomIdMapping(room_alias);
|
||||||
console.log("joining " + room_alias + " id=" + room_id);
|
console.log("joining " + room_alias + " id=" + room_id);
|
||||||
if ($rootScope.events.rooms[room_id]) {
|
if ($scope.room) { // TODO actually check that you = join
|
||||||
// don't send a join event for a room you're already in.
|
// don't send a join event for a room you're already in.
|
||||||
$location.url("room/" + room_alias);
|
$location.url("room/" + room_alias);
|
||||||
}
|
}
|
||||||
@ -576,7 +576,8 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
powerLevel = parseInt(matches[3]);
|
powerLevel = parseInt(matches[3]);
|
||||||
}
|
}
|
||||||
if (powerLevel !== NaN) {
|
if (powerLevel !== NaN) {
|
||||||
promise = matrixService.setUserPowerLevel($scope.room_id, user_id, powerLevel);
|
var powerLevelEvent = $scope.room.current_room_state.state("m.room.power_levels");
|
||||||
|
promise = matrixService.setUserPowerLevel($scope.room_id, user_id, powerLevel, powerLevelEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -591,7 +592,8 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
if (args) {
|
if (args) {
|
||||||
var matches = args.match(/^(\S+)$/);
|
var matches = args.match(/^(\S+)$/);
|
||||||
if (matches) {
|
if (matches) {
|
||||||
promise = matrixService.setUserPowerLevel($scope.room_id, args, undefined);
|
var powerLevelEvent = $scope.room.current_room_state.state("m.room.power_levels");
|
||||||
|
promise = matrixService.setUserPowerLevel($scope.room_id, args, undefined, powerLevelEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,7 +631,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
};
|
};
|
||||||
|
|
||||||
$('#mainInput').val('');
|
$('#mainInput').val('');
|
||||||
$rootScope.events.rooms[$scope.room_id].messages.push(echoMessage);
|
$scope.room.addMessageEvent(echoMessage);
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -717,6 +719,9 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
|
|
||||||
var onInit2 = function() {
|
var onInit2 = function() {
|
||||||
console.log("onInit2");
|
console.log("onInit2");
|
||||||
|
// =============================
|
||||||
|
$scope.room = modelService.getRoom($scope.room_id);
|
||||||
|
// =============================
|
||||||
|
|
||||||
// Scroll down as soon as possible so that we point to the last message
|
// Scroll down as soon as possible so that we point to the last message
|
||||||
// if it already exists in memory
|
// if it already exists in memory
|
||||||
@ -729,9 +734,9 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
var needsToJoin = true;
|
var needsToJoin = true;
|
||||||
|
|
||||||
// The room members is available in the data fetched by initialSync
|
// 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
|
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)) {
|
|| (1 === messages.length && "m.room.member" === messages[0].type && "invite" === messages[0].content.membership && $scope.state.user_id === messages[0].state_key)) {
|
||||||
@ -743,7 +748,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
$scope.state.first_pagination = false;
|
$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
|
// Update the member list
|
||||||
for (var i in members) {
|
for (var i in members) {
|
||||||
@ -999,10 +1004,15 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.openJson = function(content) {
|
$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
|
// scope this so the template can check power levels and enable/disable
|
||||||
// buttons
|
// buttons
|
||||||
$scope.pow = matrixService.getUserPowerLevel;
|
$scope.pow = eventHandlerService.getUserPowerLevel;
|
||||||
|
|
||||||
var modalInstance = $modal.open({
|
var modalInstance = $modal.open({
|
||||||
templateUrl: 'eventInfoTemplate.html',
|
templateUrl: 'eventInfoTemplate.html',
|
||||||
@ -1039,8 +1049,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
state_key: ""
|
state_key: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
var stateFilter = $filter("stateEventsFilter");
|
var stateEvents = $scope.room.current_room_state.state_events;
|
||||||
var stateEvents = stateFilter($scope.events.rooms[$scope.room_id]);
|
|
||||||
// The modal dialog will 2-way bind this field, so we MUST make a deep
|
// 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
|
// copy of the state events else we will be *actually adjusing our view
|
||||||
// of the world* when fiddling with the JSON!! Apparently parse/stringify
|
// of the world* when fiddling with the JSON!! Apparently parse/stringify
|
||||||
@ -1059,7 +1068,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
|||||||
console.log("Displaying modal dialog for >>>> " + JSON.stringify($scope.event_selected));
|
console.log("Displaying modal dialog for >>>> " + JSON.stringify($scope.event_selected));
|
||||||
$scope.redact = function() {
|
$scope.redact = function() {
|
||||||
console.log("User level = "+$scope.pow($scope.room_id, $scope.state.user_id)+
|
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));
|
console.log("Redact event >> " + JSON.stringify($scope.event_selected));
|
||||||
$modalInstance.close("redact");
|
$modalInstance.close("redact");
|
||||||
};
|
};
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button ng-click="redact()" type="button" class="btn btn-danger"
|
<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"
|
ng-disabled="!room.current_room_state.state('m.room.ops_levels').content.redact_level || !pow(room_id, state.user_id) || pow(room_id, state.user_id) < room.current_room_state.state('m.room.ops_levels').content.redact_level"
|
||||||
title="Delete this event on all home servers. This cannot be undone.">
|
title="Delete this event on all home servers. This cannot be undone.">
|
||||||
Redact
|
Redact
|
||||||
</button>
|
</button>
|
||||||
@ -18,7 +18,8 @@
|
|||||||
<table class="room-info">
|
<table class="room-info">
|
||||||
<tr ng-repeat="(key, event) in roomInfo.stateEvents" class="room-info-event">
|
<tr ng-repeat="(key, event) in roomInfo.stateEvents" class="room-info-event">
|
||||||
<td class="room-info-event-meta" width="30%">
|
<td class="room-info-event-meta" width="30%">
|
||||||
<span class="monospace">{{ key }}</span>
|
<span class="monospace">{{ event.type }}</span>
|
||||||
|
<span ng-show="event.state_key" class="monospace"> ({{event.state_key}})</span>
|
||||||
<br/>
|
<br/>
|
||||||
{{ (event.origin_server_ts) | date:'MMM d HH:mm' }}
|
{{ (event.origin_server_ts) | date:'MMM d HH:mm' }}
|
||||||
<br/>
|
<br/>
|
||||||
@ -68,13 +69,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="roomTopicSection">
|
<div class="roomTopicSection">
|
||||||
<button ng-hide="events.rooms[room_id]['m.room.topic'].content.topic || topic.isEditing"
|
<button ng-hide="room.current_room_state.state_events['m.room.topic'].content.topic || topic.isEditing"
|
||||||
ng-click="topic.editTopic()" class="roomTopicSetNew">
|
ng-click="topic.editTopic()" class="roomTopicSetNew">
|
||||||
Set Topic
|
Set Topic
|
||||||
</button>
|
</button>
|
||||||
<div ng-show="events.rooms[room_id]['m.room.topic'].content.topic || topic.isEditing">
|
<div ng-show="room.current_room_state.state_events['m.room.topic'].content.topic || topic.isEditing">
|
||||||
<div ng-hide="topic.isEditing" ng-dblclick="topic.editTopic()" id="roomTopic">
|
<div ng-hide="topic.isEditing" ng-dblclick="topic.editTopic()" id="roomTopic">
|
||||||
{{ events.rooms[room_id]['m.room.topic'].content.topic | limitTo: 200}}
|
{{ room.current_room_state.state_events['m.room.topic'].content.topic | limitTo: 200}}
|
||||||
</div>
|
</div>
|
||||||
<form ng-submit="topic.updateTopic()" ng-show="topic.isEditing" class="roomTopicForm">
|
<form ng-submit="topic.updateTopic()" ng-show="topic.isEditing" class="roomTopicForm">
|
||||||
<input ng-model="topic.newTopicText" ng-blur="topic.cancelEdit()" class="roomTopicInput" placeholder="Topic"/>
|
<input ng-model="topic.newTopicText" ng-blur="topic.cancelEdit()" class="roomTopicInput" placeholder="Topic"/>
|
||||||
@ -123,32 +124,34 @@
|
|||||||
ng-style="{ 'visibility': state.messages_visibility }"
|
ng-style="{ 'visibility': state.messages_visibility }"
|
||||||
keep-scroll>
|
keep-scroll>
|
||||||
<table id="messageTable" infinite-scroll="paginateMore()">
|
<table id="messageTable" infinite-scroll="paginateMore()">
|
||||||
<tr ng-repeat="msg in events.rooms[room_id].messages"
|
<tr ng-repeat="msg in room.events"
|
||||||
ng-class="(events.rooms[room_id].messages[$index + 1].user_id !== msg.user_id ? 'differentUser' : '') + (msg.user_id === state.user_id ? ' mine' : '')" scroll-item>
|
ng-class="(room.events[$index + 1].user_id !== msg.user_id ? 'differentUser' : '') + (msg.user_id === state.user_id ? ' mine' : '')" scroll-item>
|
||||||
<td class="leftBlock">
|
<td class="leftBlock">
|
||||||
<div class="sender" ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id"> {{ msg.user_id | mUserDisplayName: room_id }}</div>
|
<div class="sender" ng-hide="room.events[$index - 1].user_id === msg.user_id"> {{ msg.__room_member.cnt.displayname || msg.user_id | mUserDisplayName: room_id }}</div>
|
||||||
<div class="timestamp"
|
<div class="timestamp"
|
||||||
ng-class="msg.echo_msg_state">
|
ng-class="msg.echo_msg_state">
|
||||||
{{ (msg.origin_server_ts) | date:'MMM d HH:mm' }}
|
{{ (msg.origin_server_ts) | date:'MMM d HH:mm' }}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="avatar">
|
<td class="avatar">
|
||||||
<img class="avatarImage" ng-src="{{ members[msg.user_id].avatar_url || 'img/default-profile.png' }}" width="32" height="32" title="{{msg.user_id}}"
|
<!-- msg.__room_member.avatar_url is just backwards compat, and can be removed in the future. -->
|
||||||
ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id || msg.user_id === state.user_id"/>
|
<img class="avatarImage" ng-src="{{ msg.__room_member.cnt.avatar_url || msg.__room_member.avatar_url || 'img/default-profile.png' }}" width="32" height="32" title="{{msg.user_id}}"
|
||||||
|
ng-hide="room.events[$index - 1].user_id === msg.user_id || msg.user_id === state.user_id"/>
|
||||||
</td>
|
</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'">
|
<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" ng-dblclick="openJson(msg)">
|
<div class="bubble" ng-dblclick="openJson(msg)">
|
||||||
<span ng-if="'join' === msg.content.membership && msg.changedKey === 'membership'">
|
<span ng-if="'join' === msg.content.membership && msg.changedKey === 'membership'">
|
||||||
{{ members[msg.state_key].displayname || msg.state_key }} joined
|
{{ msg.content.displayname || members[msg.state_key].displayname || msg.state_key }} joined
|
||||||
</span>
|
</span>
|
||||||
<span ng-if="'leave' === msg.content.membership && msg.changedKey === 'membership'">
|
<span ng-if="'leave' === msg.content.membership && msg.changedKey === 'membership'">
|
||||||
<span ng-if="msg.user_id === msg.state_key">
|
<span ng-if="msg.user_id === msg.state_key">
|
||||||
{{ members[msg.state_key].displayname || msg.state_key }} left
|
<!-- FIXME: This seems like a synapse bug that the 'leave' content doesn't give the displayname... -->
|
||||||
|
{{ msg.__room_member.cnt.displayname || members[msg.state_key].displayname || msg.state_key }} left
|
||||||
</span>
|
</span>
|
||||||
<span ng-if="msg.user_id !== msg.state_key && msg.prev_content">
|
<span ng-if="msg.user_id !== msg.state_key && msg.prev_content">
|
||||||
{{ members[msg.user_id].displayname || msg.user_id }}
|
{{ msg.content.displayname || members[msg.user_id].displayname || msg.user_id }}
|
||||||
{{ {"invite": "kicked", "join": "kicked", "ban": "unbanned"}[msg.prev_content.membership] }}
|
{{ {"invite": "kicked", "join": "kicked", "ban": "unbanned"}[msg.prev_content.membership] }}
|
||||||
{{ members[msg.state_key].displayname || msg.state_key }}
|
{{ msg.__target_room_member.content.displayname || msg.state_key }}
|
||||||
<span ng-if="'join' === msg.prev_content.membership && msg.content.reason">
|
<span ng-if="'join' === msg.prev_content.membership && msg.content.reason">
|
||||||
: {{ msg.content.reason }}
|
: {{ msg.content.reason }}
|
||||||
</span>
|
</span>
|
||||||
@ -156,9 +159,9 @@
|
|||||||
</span>
|
</span>
|
||||||
<span ng-if="'invite' === msg.content.membership && msg.changedKey === 'membership' ||
|
<span ng-if="'invite' === msg.content.membership && msg.changedKey === 'membership' ||
|
||||||
'ban' === msg.content.membership && msg.changedKey === 'membership'">
|
'ban' === msg.content.membership && msg.changedKey === 'membership'">
|
||||||
{{ members[msg.user_id].displayname || msg.user_id }}
|
{{ msg.__room_member.cnt.displayname || msg.user_id }}
|
||||||
{{ {"invite": "invited", "ban": "banned"}[msg.content.membership] }}
|
{{ {"invite": "invited", "ban": "banned"}[msg.content.membership] }}
|
||||||
{{ members[msg.state_key].displayname || msg.state_key }}
|
{{ msg.__target_room_member.cnt.displayname || msg.state_key }}
|
||||||
<span ng-if="msg.prev_content && 'ban' === msg.prev_content.membership && msg.content.reason">
|
<span ng-if="msg.prev_content && 'ban' === msg.prev_content.membership && msg.content.reason">
|
||||||
: {{ msg.content.reason }}
|
: {{ msg.content.reason }}
|
||||||
</span>
|
</span>
|
||||||
@ -204,7 +207,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="rightBlock">
|
<td class="rightBlock">
|
||||||
<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"
|
||||||
ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id || msg.user_id !== state.user_id"/>
|
ng-hide="room.events[$index - 1].user_id === msg.user_id || msg.user_id !== state.user_id"/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
Loading…
Reference in New Issue
Block a user