summary refs log tree commit diff
diff options
context:
space:
mode:
authorKegan Dougal <kegan@matrix.org>2014-08-15 11:31:13 +0100
committerKegan Dougal <kegan@matrix.org>2014-08-15 14:06:56 +0100
commit5dbceaf5a40a7e90a4aca1a1612fa9ea13290a02 (patch)
treeafe6432266ad80f95b31390370df5278c931bf76
parentAdded event stream service which neatly blobs together requests / state for t... (diff)
downloadsynapse-5dbceaf5a40a7e90a4aca1a1612fa9ea13290a02.tar.xz
Added event handler service which.. handles events. More specifically, it $broadcasts events depending on their type, and does processing on events (shuffling keys, adding events to $rootScope so displays will automatically update, sending delivery receipts, and so on). Some of this logic was previously contained in the RoomController, which fails the moment you add >1 room into the mix, hence requiring a Service to handle events, rather than having each individual controller maintain their part of the world.
-rw-r--r--webclient/app.js3
-rw-r--r--webclient/components/matrix/event-handler-service.js87
-rw-r--r--webclient/components/matrix/event-stream-service.js17
-rw-r--r--webclient/index.html1
-rw-r--r--webclient/room/room-controller.js48
5 files changed, 124 insertions, 32 deletions
diff --git a/webclient/app.js b/webclient/app.js
index 547431d9b2..bc78eb9d17 100644
--- a/webclient/app.js
+++ b/webclient/app.js
@@ -21,7 +21,8 @@ var matrixWebClient = angular.module('matrixWebClient', [
     'RoomController',
     'RoomsController',
     'matrixService',
-    'eventStreamService'
+    'eventStreamService',
+    'eventHandlerService'
 ]);
 
 matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider',
diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js
new file mode 100644
index 0000000000..30d7ab35ca
--- /dev/null
+++ b/webclient/components/matrix/event-handler-service.js
@@ -0,0 +1,87 @@
+/*
+Copyright 2014 matrix.org
+
+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', [])
+.factory('eventHandlerService', ['matrixService', '$rootScope', function(matrixService, $rootScope) {
+    var MSG_EVENT = "MSG_EVENT";
+    var MEMBER_EVENT = "MEMBER_EVENT";
+    var PRESENCE_EVENT = "PRESENCE_EVENT";
+    
+    var handleMessage = function(event, isLiveEvent) {
+        if ("membership_target" in event.content) {
+            // event.user_id = event.content.membership_target;
+        }
+        
+        // $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.
+        console.log("Bcast " + JSON.stringify(event));
+        $rootScope.$broadcast(MSG_EVENT, event, isLiveEvent);
+    };
+    
+    var handleRoomMember = function(event, isLiveEvent) {
+        $rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent);
+    };
+    
+    var handlePresence = function(event, isLiveEvent) {
+        $rootScope.$broadcast(PRESENCE_EVENT, event, isLiveEvent);
+    };
+    
+    
+    return {
+        MSG_EVENT: MSG_EVENT,
+        MEMBER_EVENT: MEMBER_EVENT,
+        PRESENCE_EVENT: PRESENCE_EVENT,
+        
+    
+        handleEvent: function(event, isLiveEvent) {
+            switch(event.type) {
+                case "m.room.message":
+                    handleMessage(event, isLiveEvent);
+                    break;
+                case "m.room.member":
+                    handleRoomMember(event, isLiveEvent);
+                    break;
+                case "m.presence":
+                    handlePresence(event, isLiveEvent);
+                    break;
+                default:
+                    console.log("Unable to handle event type " + event.type);
+                    break;
+            }
+        },
+        
+        // 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);
+            }
+        }
+    };
+}]);
diff --git a/webclient/components/matrix/event-stream-service.js b/webclient/components/matrix/event-stream-service.js
index 0a3a12192b..1cb9960b9a 100644
--- a/webclient/components/matrix/event-stream-service.js
+++ b/webclient/components/matrix/event-stream-service.js
@@ -16,8 +16,17 @@ limitations under the License.
 
 'use strict';
 
+/*
+This service manages where in the event stream the web client currently is and 
+provides methods to resume/pause/stop the event stream. This service is not
+responsible for parsing event data. For that, see the eventDataHandler.
+*/
 angular.module('eventStreamService', [])
 .factory('eventStreamService', ['matrixService', function(matrixService) {
+    var END = "END";
+    var START = "START";
+    var TIMEOUT_MS = 5000;
+    
     var settings = {
         from: "END",
         to: undefined,
@@ -28,7 +37,7 @@ angular.module('eventStreamService', [])
     // interrupts the stream. Only valid if there is a stream conneciton 
     // open.
     var interrupt = function(shouldPoll) {
-        console.log("[EventStream] interrupt("+shouldPoll+") "+
+        console.log("p[EventStream] interrupt("+shouldPoll+") "+
                     JSON.stringify(settings));
     };
     
@@ -42,7 +51,7 @@ angular.module('eventStreamService', [])
         resume: function() {
             console.log("[EventStream] resume "+JSON.stringify(settings));
             // run the stream from the latest token
-            return matrixService.getEventStream(settings.from, 5000);
+            return matrixService.getEventStream(settings.from, TIMEOUT_MS);
         },
         
         // pause the stream. Resuming it will continue from the current position
@@ -55,13 +64,13 @@ angular.module('eventStreamService', [])
         },
         
         // stop the stream and wipe the position in the stream. Typically used
-        // when logging out.
+        // when logging out / logged out.
         stop: function() {
             console.log("[EventStream] stop "+JSON.stringify(settings));
             // kill any running stream
             interrupt(false);
             // clear the latest token
-            settings.from = "END";
+            settings.from = END;
             saveStreamSettings();
         }
     };
diff --git a/webclient/index.html b/webclient/index.html
index 793b03d108..31b62efaa8 100644
--- a/webclient/index.html
+++ b/webclient/index.html
@@ -15,6 +15,7 @@
     <script src="rooms/rooms-controller.js"></script>
     <script src="components/matrix/matrix-service.js"></script>
     <script src="components/matrix/event-stream-service.js"></script>
+    <script src="components/matrix/event-handler-service.js"></script>
     <script src="components/fileInput/file-input-directive.js"></script>
     <script src="components/fileUpload/file-upload-service.js"></script>
 </head>
diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js
index 3f69a12c23..301d3d6447 100644
--- a/webclient/room/room-controller.js
+++ b/webclient/room/room-controller.js
@@ -15,8 +15,8 @@ limitations under the License.
 */
 
 angular.module('RoomController', [])
-.controller('RoomController', ['$scope', '$http', '$timeout', '$routeParams', '$location', 'matrixService', 'eventStreamService',
-                               function($scope, $http, $timeout, $routeParams, $location, matrixService, eventStreamService) {
+.controller('RoomController', ['$scope', '$http', '$timeout', '$routeParams', '$location', 'matrixService', 'eventStreamService', 'eventHandlerService',
+                               function($scope, $http, $timeout, $routeParams, $location, matrixService, eventStreamService, eventHandlerService) {
    'use strict';
     var MESSAGES_PER_PAGINATION = 10;
     $scope.room_id = $routeParams.room_id;
@@ -42,34 +42,28 @@ angular.module('RoomController', [])
         },0);
     };
     
-    var parseChunk = function(chunks, appendToStart) {
-        for (var i = 0; i < chunks.length; i++) {
-            var chunk = chunks[i];
-            if (chunk.room_id == $scope.room_id && chunk.type == "m.room.message") {
-                if ("membership_target" in chunk.content) {
-                    chunk.user_id = chunk.content.membership_target;
-                }
-                if (appendToStart) {
-                    $scope.messages.unshift(chunk);
-                }
-                else {
-                    $scope.messages.push(chunk);
-                    scrollToBottom();
-                }
-            }
-            else if (chunk.room_id == $scope.room_id && chunk.type == "m.room.member") {
-                updateMemberList(chunk);
-            }
-            else if (chunk.type === "m.presence") {
-                updatePresence(chunk);
-            }
+    $scope.$on(eventHandlerService.MSG_EVENT, function(ngEvent, event, isLive) {
+        if (isLive) {
+            $scope.messages.push(event);
+            scrollToBottom();
         }
-    };
+        else {
+            $scope.messages.unshift(event);
+        }
+    });
+    
+    $scope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) {
+        updateMemberList(event);
+    });
+    
+    $scope.$on(eventHandlerService.PRESENCE_EVENT, function(ngEvent, event, isLive) {
+        updatePresence(event);
+    });
     
     var paginate = function(numItems) {
         matrixService.paginateBackMessages($scope.room_id, $scope.state.earliest_token, numItems).then(
             function(response) {
-                parseChunk(response.data.chunk, true);
+                eventHandlerService.handleEvents(response.data.chunk, false);
                 $scope.state.earliest_token = response.data.end;
                 if (response.data.chunk.length < MESSAGES_PER_PAGINATION) {
                     // no more messages to paginate :(
@@ -89,8 +83,8 @@ angular.module('RoomController', [])
                 console.log("Got response from "+$scope.state.events_from+" to "+response.data.end);
                 $scope.state.events_from = response.data.end;
                 $scope.feedback = "";
-
-                parseChunk(response.data.chunk, false);
+                
+                eventHandlerService.handleEvents(response.data.chunk, true);
                 
                 if ($scope.stopPoll) {
                     console.log("Stopping polling.");