2014-08-15 04:31:13 -06:00
|
|
|
/*
|
2014-09-03 10:29:13 -06:00
|
|
|
Copyright 2014 OpenMarket Ltd
|
2014-08-15 04:31:13 -06:00
|
|
|
|
|
|
|
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 handles what should happen when you get an event. This service does
|
|
|
|
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
|
|
|
|
direct GETs or via a pagination stream API), etc.
|
|
|
|
|
|
|
|
Typically, this service will store events or broadcast them to any listeners
|
|
|
|
(e.g. controllers) via $broadcast. Alternatively, it may update the $rootScope
|
|
|
|
if typically all the $on method would do is update its own $scope.
|
|
|
|
*/
|
|
|
|
angular.module('eventHandlerService', [])
|
2014-08-28 08:22:35 -06:00
|
|
|
.factory('eventHandlerService', ['matrixService', '$rootScope', '$q', function(matrixService, $rootScope, $q) {
|
2014-09-02 05:55:14 -06:00
|
|
|
var ROOM_CREATE_EVENT = "ROOM_CREATE_EVENT";
|
2014-08-15 04:31:13 -06:00
|
|
|
var MSG_EVENT = "MSG_EVENT";
|
|
|
|
var MEMBER_EVENT = "MEMBER_EVENT";
|
|
|
|
var PRESENCE_EVENT = "PRESENCE_EVENT";
|
2014-09-03 06:12:56 -06:00
|
|
|
var POWERLEVEL_EVENT = "POWERLEVEL_EVENT";
|
2014-08-29 06:23:01 -06:00
|
|
|
var CALL_EVENT = "CALL_EVENT";
|
2014-09-03 07:40:54 -06:00
|
|
|
var NAME_EVENT = "NAME_EVENT";
|
2014-08-28 08:22:35 -06:00
|
|
|
|
2014-09-08 10:13:22 -06:00
|
|
|
var initialSyncDeferred = $q.defer();
|
2014-08-15 04:31:13 -06:00
|
|
|
|
2014-08-15 05:51:20 -06:00
|
|
|
$rootScope.events = {
|
2014-08-28 08:22:35 -06:00
|
|
|
rooms: {} // will contain roomId: { messages:[], members:{userid1: event} }
|
2014-08-15 05:51:20 -06:00
|
|
|
};
|
2014-09-06 01:36:55 -06:00
|
|
|
|
|
|
|
// used for dedupping events - could be expanded in future...
|
|
|
|
// FIXME: means that we leak memory over time (along with lots of the rest
|
|
|
|
// of the app, given we never try to reap memory yet)
|
|
|
|
var eventMap = {};
|
2014-08-22 03:50:38 -06:00
|
|
|
|
|
|
|
$rootScope.presence = {};
|
2014-08-15 05:51:20 -06:00
|
|
|
|
|
|
|
var initRoom = function(room_id) {
|
2014-08-15 08:40:37 -06:00
|
|
|
if (!(room_id in $rootScope.events.rooms)) {
|
|
|
|
console.log("Creating new handler entry for " + room_id);
|
|
|
|
$rootScope.events.rooms[room_id] = {};
|
|
|
|
$rootScope.events.rooms[room_id].messages = [];
|
|
|
|
$rootScope.events.rooms[room_id].members = {};
|
|
|
|
}
|
2014-09-02 05:55:14 -06:00
|
|
|
};
|
2014-08-22 03:50:10 -06:00
|
|
|
|
2014-08-28 08:22:35 -06:00
|
|
|
var resetRoomMessages = function(room_id) {
|
|
|
|
if ($rootScope.events.rooms[room_id]) {
|
|
|
|
$rootScope.events.rooms[room_id].messages = [];
|
|
|
|
}
|
|
|
|
};
|
2014-08-15 05:51:20 -06:00
|
|
|
|
2014-09-02 05:55:14 -06:00
|
|
|
var handleRoomCreate = function(event, isLiveEvent) {
|
|
|
|
initRoom(event.room_id);
|
|
|
|
|
|
|
|
// For now, we do not use the event data. Simply signal it to the app controllers
|
|
|
|
$rootScope.$broadcast(ROOM_CREATE_EVENT, event, isLiveEvent);
|
|
|
|
};
|
|
|
|
|
2014-09-06 01:31:57 -06:00
|
|
|
var handleRoomAliases = function(event, isLiveEvent) {
|
|
|
|
matrixService.createRoomIdToAliasMapping(event.room_id, event.content.aliases[0]);
|
|
|
|
};
|
|
|
|
|
2014-08-15 04:31:13 -06:00
|
|
|
var handleMessage = function(event, isLiveEvent) {
|
2014-08-15 08:40:37 -06:00
|
|
|
initRoom(event.room_id);
|
2014-08-15 04:31:13 -06:00
|
|
|
|
2014-08-15 05:51:20 -06:00
|
|
|
if (isLiveEvent) {
|
2014-09-06 11:13:38 -06:00
|
|
|
if (event.user_id === matrixService.config().user_id &&
|
|
|
|
(event.content.msgtype === "m.text" || event.content.msgtype === "m.emote") ) {
|
2014-09-06 01:31:57 -06:00
|
|
|
// assume we've already echoed it
|
|
|
|
// FIXME: track events by ID and ungrey the right message to show it's been delivered
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$rootScope.events.rooms[event.room_id].messages.push(event);
|
|
|
|
}
|
2014-08-15 05:51:20 -06:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
$rootScope.events.rooms[event.room_id].messages.unshift(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO send delivery receipt if isLiveEvent
|
|
|
|
|
2014-08-15 04:31:13 -06:00
|
|
|
// $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);
|
|
|
|
};
|
|
|
|
|
|
|
|
var handleRoomMember = function(event, isLiveEvent) {
|
2014-08-15 08:40:37 -06:00
|
|
|
initRoom(event.room_id);
|
2014-08-21 12:02:00 -06:00
|
|
|
|
2014-09-06 01:31:57 -06:00
|
|
|
// if the server is stupidly re-relaying a no-op join, discard it.
|
|
|
|
if (event.prev_content &&
|
|
|
|
event.content.membership === "join" &&
|
|
|
|
event.content.membership === event.prev_content.membership)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-21 12:02:00 -06:00
|
|
|
// add membership changes as if they were a room message if something interesting changed
|
|
|
|
if (event.content.prev !== event.content.membership) {
|
|
|
|
if (isLiveEvent) {
|
|
|
|
$rootScope.events.rooms[event.room_id].messages.push(event);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$rootScope.events.rooms[event.room_id].messages.unshift(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-03 11:02:51 -06:00
|
|
|
$rootScope.events.rooms[event.room_id].members[event.state_key] = event;
|
2014-08-15 04:31:13 -06:00
|
|
|
$rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent);
|
|
|
|
};
|
|
|
|
|
|
|
|
var handlePresence = function(event, isLiveEvent) {
|
2014-08-22 03:50:38 -06:00
|
|
|
$rootScope.presence[event.content.user_id] = event;
|
2014-08-15 04:31:13 -06:00
|
|
|
$rootScope.$broadcast(PRESENCE_EVENT, event, isLiveEvent);
|
|
|
|
};
|
2014-09-02 03:54:11 -06:00
|
|
|
|
|
|
|
var handlePowerLevels = function(event, isLiveEvent) {
|
|
|
|
initRoom(event.room_id);
|
|
|
|
|
2014-09-03 07:00:04 -06:00
|
|
|
// Keep the latest data. Do not care of events that come when paginating back
|
|
|
|
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);
|
|
|
|
}
|
2014-09-02 03:54:11 -06:00
|
|
|
};
|
2014-08-29 06:23:01 -06:00
|
|
|
|
2014-09-03 07:40:54 -06:00
|
|
|
var handleRoomName = function(event, isLiveEvent) {
|
|
|
|
console.log("handleRoomName " + isLiveEvent);
|
|
|
|
|
|
|
|
initRoom(event.room_id);
|
|
|
|
|
|
|
|
$rootScope.events.rooms[event.room_id][event.type] = event;
|
|
|
|
$rootScope.$broadcast(NAME_EVENT, event, isLiveEvent);
|
|
|
|
};
|
2014-09-08 16:36:52 -06:00
|
|
|
|
2014-09-08 19:40:34 -06:00
|
|
|
// TODO: Can this just be a generic "I am a room state event, can haz store?"
|
2014-09-08 16:36:52 -06:00
|
|
|
var handleRoomTopic = function(event, isLiveEvent) {
|
2014-09-08 18:27:51 -06:00
|
|
|
console.log("handleRoomTopic live="+isLiveEvent);
|
2014-09-08 16:36:52 -06:00
|
|
|
|
|
|
|
initRoom(event.room_id);
|
|
|
|
|
2014-09-08 18:27:51 -06:00
|
|
|
// live events always update, but non-live events only update if the
|
|
|
|
// ts is later.
|
|
|
|
if (!isLiveEvent) {
|
|
|
|
var eventTs = event.ts;
|
|
|
|
var storedEvent = $rootScope.events.rooms[event.room_id][event.type];
|
|
|
|
if (storedEvent) {
|
|
|
|
if (storedEvent.ts > eventTs) {
|
|
|
|
// ignore it, we have a newer one already.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-09-08 16:36:52 -06:00
|
|
|
$rootScope.events.rooms[event.room_id][event.type] = event;
|
|
|
|
};
|
2014-09-03 07:40:54 -06:00
|
|
|
|
2014-08-29 06:23:01 -06:00
|
|
|
var handleCallEvent = function(event, isLiveEvent) {
|
|
|
|
$rootScope.$broadcast(CALL_EVENT, event, isLiveEvent);
|
2014-09-09 04:45:36 -06:00
|
|
|
if (event.type == 'm.call.invite') {
|
|
|
|
$rootScope.events.rooms[event.room_id].messages.push(event);
|
|
|
|
}
|
2014-08-29 06:23:01 -06:00
|
|
|
};
|
2014-08-15 04:31:13 -06:00
|
|
|
|
|
|
|
return {
|
2014-09-02 05:55:14 -06:00
|
|
|
ROOM_CREATE_EVENT: ROOM_CREATE_EVENT,
|
2014-08-15 04:31:13 -06:00
|
|
|
MSG_EVENT: MSG_EVENT,
|
|
|
|
MEMBER_EVENT: MEMBER_EVENT,
|
|
|
|
PRESENCE_EVENT: PRESENCE_EVENT,
|
2014-09-03 06:12:56 -06:00
|
|
|
POWERLEVEL_EVENT: POWERLEVEL_EVENT,
|
2014-08-29 06:23:01 -06:00
|
|
|
CALL_EVENT: CALL_EVENT,
|
2014-09-03 07:40:54 -06:00
|
|
|
NAME_EVENT: NAME_EVENT,
|
2014-08-15 04:31:13 -06:00
|
|
|
|
|
|
|
handleEvent: function(event, isLiveEvent) {
|
2014-09-06 11:13:38 -06:00
|
|
|
// FIXME: event duplication suppression is all broken as the code currently expect to handles
|
|
|
|
// events multiple times to get their side-effects...
|
|
|
|
/*
|
2014-09-06 01:36:55 -06:00
|
|
|
if (eventMap[event.event_id]) {
|
|
|
|
console.log("discarding duplicate event: " + JSON.stringify(event));
|
|
|
|
return;
|
2014-08-15 04:31:13 -06:00
|
|
|
}
|
2014-09-06 11:13:38 -06:00
|
|
|
else {
|
|
|
|
eventMap[event.event_id] = 1;
|
|
|
|
}
|
|
|
|
*/
|
2014-09-02 05:55:14 -06:00
|
|
|
if (event.type.indexOf('m.call.') === 0) {
|
2014-08-29 06:23:01 -06:00
|
|
|
handleCallEvent(event, isLiveEvent);
|
|
|
|
}
|
2014-09-06 01:36:55 -06:00
|
|
|
else {
|
|
|
|
switch(event.type) {
|
|
|
|
case "m.room.create":
|
|
|
|
handleRoomCreate(event, isLiveEvent);
|
|
|
|
break;
|
|
|
|
case "m.room.aliases":
|
|
|
|
handleRoomAliases(event, isLiveEvent);
|
|
|
|
break;
|
|
|
|
case "m.room.message":
|
|
|
|
handleMessage(event, isLiveEvent);
|
|
|
|
break;
|
|
|
|
case "m.room.member":
|
|
|
|
handleRoomMember(event, isLiveEvent);
|
|
|
|
break;
|
|
|
|
case "m.presence":
|
|
|
|
handlePresence(event, isLiveEvent);
|
|
|
|
break;
|
|
|
|
case 'm.room.ops_levels':
|
|
|
|
case 'm.room.send_event_level':
|
|
|
|
case 'm.room.add_state_level':
|
|
|
|
case 'm.room.join_rules':
|
|
|
|
case 'm.room.power_levels':
|
|
|
|
handlePowerLevels(event, isLiveEvent);
|
|
|
|
break;
|
|
|
|
case 'm.room.name':
|
|
|
|
handleRoomName(event, isLiveEvent);
|
|
|
|
break;
|
2014-09-08 16:36:52 -06:00
|
|
|
case 'm.room.topic':
|
|
|
|
handleRoomTopic(event, isLiveEvent);
|
|
|
|
break;
|
2014-09-06 01:36:55 -06:00
|
|
|
default:
|
|
|
|
console.log("Unable to handle event type " + event.type);
|
|
|
|
console.log(JSON.stringify(event, undefined, 4));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-08-15 04:31:13 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
// isLiveEvents determines whether notifications should be shown, whether
|
|
|
|
// messages get appended to the start/end of lists, etc.
|
|
|
|
handleEvents: function(events, isLiveEvents) {
|
|
|
|
for (var i=0; i<events.length; i++) {
|
|
|
|
this.handleEvent(events[i], isLiveEvents);
|
|
|
|
}
|
2014-08-22 03:50:10 -06:00
|
|
|
},
|
|
|
|
|
2014-09-08 10:13:22 -06:00
|
|
|
handleInitialSyncDone: function(initialSyncData) {
|
2014-08-28 08:22:35 -06:00
|
|
|
console.log("# handleInitialSyncDone");
|
2014-09-08 10:13:22 -06:00
|
|
|
initialSyncDeferred.resolve(initialSyncData);
|
2014-08-22 03:50:10 -06:00
|
|
|
},
|
2014-08-28 08:22:35 -06:00
|
|
|
|
|
|
|
// Returns a promise that resolves when the initialSync request has been processed
|
|
|
|
waitForInitialSyncCompletion: function() {
|
2014-09-08 10:13:22 -06:00
|
|
|
return initialSyncDeferred.promise;
|
2014-08-28 08:22:35 -06:00
|
|
|
},
|
|
|
|
|
|
|
|
resetRoomMessages: function(room_id) {
|
|
|
|
resetRoomMessages(room_id);
|
|
|
|
}
|
2014-08-15 04:31:13 -06:00
|
|
|
};
|
|
|
|
}]);
|