From 7dac1bfc9148e4e23d388d8281aacee2bb41d5db Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 21 Aug 2014 17:17:41 +0100 Subject: Change webclient to always hit the im sync api before streaming so we get current presence state --- .../components/matrix/event-stream-service.js | 41 ++++++++++++++++++---- 1 file changed, 35 insertions(+), 6 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-stream-service.js b/webclient/components/matrix/event-stream-service.js index a446fad5d4..9a8f6eac4c 100644 --- a/webclient/components/matrix/event-stream-service.js +++ b/webclient/components/matrix/event-stream-service.js @@ -48,11 +48,12 @@ angular.module('eventStreamService', []) var saveStreamSettings = function() { localStorage.setItem("streamSettings", JSON.stringify(settings)); }; - - var startEventStream = function() { + + var doEventStream = function(deferred) { settings.shouldPoll = true; settings.isActive = true; - var deferred = $q.defer(); + deferred = deferred || $q.defer(); + // run the stream from the latest token matrixService.getEventStream(settings.from, TIMEOUT_MS).then( function(response) { @@ -63,13 +64,16 @@ angular.module('eventStreamService', []) settings.from = response.data.end; - console.log("[EventStream] Got response from "+settings.from+" to "+response.data.end); + console.log( + "[EventStream] Got response from "+settings.from+ + " to "+response.data.end + ); eventHandlerService.handleEvents(response.data.chunk, true); deferred.resolve(response); if (settings.shouldPoll) { - $timeout(startEventStream, 0); + $timeout(doEventStream, 0); } else { console.log("[EventStream] Stopping poll."); @@ -83,13 +87,38 @@ angular.module('eventStreamService', []) deferred.reject(error); if (settings.shouldPoll) { - $timeout(startEventStream, ERR_TIMEOUT_MS); + $timeout(doEventStream, ERR_TIMEOUT_MS); } else { console.log("[EventStream] Stopping polling."); } } ); + + return deferred.promise; + } + + var startEventStream = function() { + settings.shouldPoll = true; + settings.isActive = true; + var deferred = $q.defer(); + + // FIXME: We are discarding all the messages. + matrixService.rooms().then( + function(response) { + var presence = response.data.presence; + for (var i = 0; i < presence.length; ++i) { + eventHandlerService.handleEvent(presence[i], false); + } + + settings.from = response.data.end + doEventStream(deferred); + }, + function(error) { + $scope.feedback = "Failure: " + error.data; + } + ); + return deferred.promise; }; -- cgit 1.5.1 From 2e1ab9db08e3fe41822a65fdf38feafbd22173b6 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 21 Aug 2014 17:55:41 +0100 Subject: Only start event streaming after having set up the controllers. --- demo/start.sh | 3 ++- webclient/app-controller.js | 2 +- webclient/app.js | 2 +- webclient/components/matrix/event-stream-service.js | 10 ++++++++++ webclient/room/room-controller.js | 1 + webclient/rooms/rooms-controller.js | 6 ++++-- 6 files changed, 19 insertions(+), 5 deletions(-) (limited to 'webclient/components') diff --git a/demo/start.sh b/demo/start.sh index 4701872926..fa2998a5e9 100755 --- a/demo/start.sh +++ b/demo/start.sh @@ -15,7 +15,8 @@ for port in "8080" "8081" "8082"; do -f "$DIR/$port.log" \ -d "$DIR/$port.db" \ -vv \ - -D --pid-file "$DIR/$port.pid" + -D --pid-file "$DIR/$port.pid"\ + -w done echo "Starting webclient on port 8000..." diff --git a/webclient/app-controller.js b/webclient/app-controller.js index 96656e12c3..c53f29aa77 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -53,7 +53,7 @@ angular.module('MatrixWebClientController', ['matrixService']) }; if (matrixService.isUserLoggedIn()) { - eventStreamService.resume(); + // eventStreamService.resume(); } // Logs the user out diff --git a/webclient/app.js b/webclient/app.js index f27ebedc6f..944b8ec270 100644 --- a/webclient/app.js +++ b/webclient/app.js @@ -80,6 +80,6 @@ matrixWebClient.run(['$location', 'matrixService', 'eventStreamService', functio $location.path("login"); } else { - eventStreamService.resume(); + // eventStreamService.resume(); } }]); diff --git a/webclient/components/matrix/event-stream-service.js b/webclient/components/matrix/event-stream-service.js index 9a8f6eac4c..a1a98b2a36 100644 --- a/webclient/components/matrix/event-stream-service.js +++ b/webclient/components/matrix/event-stream-service.js @@ -106,6 +106,16 @@ angular.module('eventStreamService', []) // FIXME: We are discarding all the messages. matrixService.rooms().then( function(response) { + var rooms = response.data.rooms; + for (var i = 0; i < rooms.length; ++i) { + var room = rooms[i]; + if ("state" in room) { + for (var j = 0; j < room.state.length; ++j) { + eventHandlerService.handleEvents(room.state[j], false); + } + } + } + var presence = response.data.presence; for (var i = 0; i < presence.length; ++i) { eventHandlerService.handleEvent(presence[i], false); diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index eee805dafb..214166a434 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -328,6 +328,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) var chunk = response.data.chunk[i]; updateMemberList(chunk); } + eventStreamService.resume(); }, function(error) { $scope.feedback = "Failed get member list: " + error.data.error; diff --git a/webclient/rooms/rooms-controller.js b/webclient/rooms/rooms-controller.js index 6bbb2b2ba1..c2d7bcb6f3 100644 --- a/webclient/rooms/rooms-controller.js +++ b/webclient/rooms/rooms-controller.js @@ -17,8 +17,8 @@ limitations under the License. 'use strict'; angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', 'eventHandlerService']) -.controller('RoomsController', ['$scope', '$location', 'matrixService', 'mFileUpload', 'eventHandlerService', - function($scope, $location, matrixService, mFileUpload, eventHandlerService) { +.controller('RoomsController', ['$scope', '$location', 'matrixService', 'mFileUpload', 'eventHandlerService', 'eventStreamService', + function($scope, $location, matrixService, mFileUpload, eventHandlerService, eventStreamService) { $scope.rooms = {}; $scope.public_rooms = []; @@ -113,6 +113,8 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', $scope.public_rooms = assignRoomAliases(response.data.chunk); } ); + + eventStreamService.resume(); }; $scope.createNewRoom = function(room_id, isPrivate) { -- cgit 1.5.1 From 3277a650529d4ecaf816987e6cbcb87fdf3371da Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 21 Aug 2014 19:02:00 +0100 Subject: actually display room metadata based on m.room.membe events --- webclient/app.css | 4 ++++ webclient/components/matrix/event-handler-service.js | 11 +++++++++++ webclient/room/room.html | 10 ++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) (limited to 'webclient/components') diff --git a/webclient/app.css b/webclient/app.css index 83b0c9c65a..a63b5db4d6 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -245,6 +245,10 @@ h1 { background-color: #fff ! important; } +.mine .membership { + background-color: #fff ! important; +} + .mine .text .bubble { text-align: left ! important; } diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index b8529895fe..6a01b3fb55 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -69,6 +69,17 @@ angular.module('eventHandlerService', []) var handleRoomMember = function(event, isLiveEvent) { initRoom(event.room_id); + + // 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); + } + } + $rootScope.events.rooms[event.room_id].members[event.user_id] = event; $rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent); }; diff --git a/webclient/room/room.html b/webclient/room/room.html index cb9cf1d1f3..4a07dfdaaf 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -26,19 +26,25 @@
+ -
{{ members[msg.user_id].displayname || msg.user_id }}
-
{{ msg.content.hsob_ts | date:'MMM d HH:mm:ss' }}
+
{{ (msg.content.hsob_ts || msg.ts) | date:'MMM d HH:mm:ss' }}
+
+ + {{ members[msg.user_id].displayname || msg.user_id }} + {{ {"join": "joined", "leave": "left", "invite": "invited"}[msg.content.membership] }} + {{ msg.content.target_id || '' }} +
-- cgit 1.5.1 From c8d0c4762da432aafa4372928aa70ef55646134b Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 22 Aug 2014 10:15:15 +0200 Subject: Safari needs the img.onload event before actually working on the img --- .../components/fileUpload/file-upload-service.js | 1 + .../components/utilities/utilities-service.js | 65 +++++++++++++--------- 2 files changed, 39 insertions(+), 27 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/fileUpload/file-upload-service.js b/webclient/components/fileUpload/file-upload-service.js index 6606f31e22..398124fcc4 100644 --- a/webclient/components/fileUpload/file-upload-service.js +++ b/webclient/components/fileUpload/file-upload-service.js @@ -82,6 +82,7 @@ angular.module('mFileUpload', ['matrixService', 'mUtilities']) // First, get the image size mUtilities.getImageSize(imageFile).then( function(size) { + console.log("image size: " + JSON.stringify(size)); // The final operation: send imageFile var uploadImage = function() { diff --git a/webclient/components/utilities/utilities-service.js b/webclient/components/utilities/utilities-service.js index 9cf858ef39..5e9f707221 100644 --- a/webclient/components/utilities/utilities-service.js +++ b/webclient/components/utilities/utilities-service.js @@ -38,10 +38,15 @@ angular.module('mUtilities', []) img.src = e.target.result; // Once ready, returns its size - deferred.resolve({ - width: img.width, - height: img.height - }); + img.onload = function() { + deferred.resolve({ + width: img.width, + height: img.height + }); + }; + img.onerror = function(e) { + deferred.reject(e); + }; }; reader.onerror = function(e) { deferred.reject(e); @@ -71,33 +76,39 @@ angular.module('mUtilities', []) reader.onload = function(e) { img.src = e.target.result; + + // Once ready, returns its size + img.onload = function() { + var ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0); - var ctx = canvas.getContext("2d"); - ctx.drawImage(img, 0, 0); - - var MAX_WIDTH = maxSize; - var MAX_HEIGHT = maxSize; - var width = img.width; - var height = img.height; + var MAX_WIDTH = maxSize; + var MAX_HEIGHT = maxSize; + var width = img.width; + var height = img.height; - if (width > height) { - if (width > MAX_WIDTH) { - height *= MAX_WIDTH / width; - width = MAX_WIDTH; - } - } else { - if (height > MAX_HEIGHT) { - width *= MAX_HEIGHT / height; - height = MAX_HEIGHT; + if (width > height) { + if (width > MAX_WIDTH) { + height *= MAX_WIDTH / width; + width = MAX_WIDTH; + } + } else { + if (height > MAX_HEIGHT) { + width *= MAX_HEIGHT / height; + height = MAX_HEIGHT; + } } - } - canvas.width = width; - canvas.height = height; - var ctx = canvas.getContext("2d"); - ctx.drawImage(img, 0, 0, width, height); + canvas.width = width; + canvas.height = height; + var ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0, width, height); - var dataUrl = canvas.toDataURL("image/jpeg", 0.7); - deferred.resolve(self.dataURItoBlob(dataUrl)); + var dataUrl = canvas.toDataURL("image/jpeg", 0.7); + deferred.resolve(self.dataURItoBlob(dataUrl)); + }; + img.onerror = function(e) { + deferred.reject(e); + }; }; reader.onerror = function(e) { deferred.reject(e); -- cgit 1.5.1 From 53f4fbd99a223a4321377ea670d1d19b669b6f4a Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 22 Aug 2014 10:48:00 +0200 Subject: resizeImage: generate an image in the format of the original image. (Tested with tranparent PNG, transparent GIF, BMP, JPEG) --- webclient/components/utilities/utilities-service.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'webclient/components') diff --git a/webclient/components/utilities/utilities-service.js b/webclient/components/utilities/utilities-service.js index 5e9f707221..3df2f04458 100644 --- a/webclient/components/utilities/utilities-service.js +++ b/webclient/components/utilities/utilities-service.js @@ -103,7 +103,9 @@ angular.module('mUtilities', []) var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, width, height); - var dataUrl = canvas.toDataURL("image/jpeg", 0.7); + // Extract image data in the same format as the original one. + // The 0.7 compression value will work with formats that supports it like JPEG. + var dataUrl = canvas.toDataURL(imageFile.type, 0.7); deferred.resolve(self.dataURItoBlob(dataUrl)); }; img.onerror = function(e) { -- cgit 1.5.1 From acf51276042cf438cbb02bb5ef31c42206d7685d Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 10:25:27 +0100 Subject: Make the content repo work with in daemon mode. Return the full url on upload. Update the webclient to use new content repo api. --- synapse/app/homeserver.py | 5 +++-- synapse/http/server.py | 26 +++++++++++++++++----- .../components/fileUpload/file-upload-service.js | 2 +- 3 files changed, 24 insertions(+), 9 deletions(-) (limited to 'webclient/components') diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index 6b39da4a7d..495149466c 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -56,7 +56,7 @@ class SynapseHomeServer(HomeServer): return File("webclient") # TODO configurable? def build_resource_for_content_repo(self): - return ContentRepoResource("uploads", self.auth) + return ContentRepoResource(self, self.upload_dir, self.auth) def build_db_pool(self): """ Set up all the dbs. Since all the *.sql have IF NOT EXISTS, so we @@ -257,7 +257,8 @@ def setup(): hs = SynapseHomeServer( args.host, - db_name=db_name + upload_dir=os.path.abspath("uploads"), + db_name=db_name, ) # This object doesn't need to be saved because it's set as the handler for diff --git a/synapse/http/server.py b/synapse/http/server.py index c28d9a33f9..d1f99460c1 100644 --- a/synapse/http/server.py +++ b/synapse/http/server.py @@ -212,8 +212,9 @@ class ContentRepoResource(resource.Resource): """ isLeaf = True - def __init__(self, directory, auth): + def __init__(self, hs, directory, auth): resource.Resource.__init__(self) + self.hs = hs self.directory = directory self.auth = auth @@ -250,7 +251,8 @@ class ContentRepoResource(resource.Resource): file_ext = re.sub("[^a-z]", "", file_ext) suffix += "." + file_ext - file_path = os.path.join(self.directory, prefix + main_part + suffix) + file_name = prefix + main_part + suffix + file_path = os.path.join(self.directory, file_name) logger.info("User %s is uploading a file to path %s", auth_user.to_string(), file_path) @@ -259,8 +261,8 @@ class ContentRepoResource(resource.Resource): attempts = 0 while os.path.exists(file_path): main_part = random_string(24) - file_path = os.path.join(self.directory, - prefix + main_part + suffix) + file_name = prefix + main_part + suffix + file_path = os.path.join(self.directory, file_name) attempts += 1 if attempts > 25: # really? Really? raise SynapseError(500, "Unable to create file.") @@ -272,11 +274,14 @@ class ContentRepoResource(resource.Resource): # servers. # TODO: A little crude here, we could do this better. - filename = request.path.split(self.directory + "/")[1] + filename = request.path.split('/')[-1] # be paranoid filename = re.sub("[^0-9A-z.-_]", "", filename) file_path = self.directory + "/" + filename + + logger.debug("Searching for %s", file_path) + if os.path.isfile(file_path): # filename has the content type base64_contentype = filename.split(".")[1] @@ -304,6 +309,10 @@ class ContentRepoResource(resource.Resource): self._async_render(request) return server.NOT_DONE_YET + def render_OPTIONS(self, request): + respond_with_json_bytes(request, 200, {}, send_cors=True) + return server.NOT_DONE_YET + @defer.inlineCallbacks def _async_render(self, request): try: @@ -313,8 +322,13 @@ class ContentRepoResource(resource.Resource): with open(fname, "wb") as f: f.write(request.content.read()) + + # FIXME (erikj): These should use constants. + file_name = os.path.basename(fname) + url = "http://%s/matrix/content/%s" % (self.hs.hostname, file_name) + respond_with_json_bytes(request, 200, - json.dumps({"content_token": fname}), + json.dumps({"content_token": url}), send_cors=True) except CodeMessageException as e: diff --git a/webclient/components/fileUpload/file-upload-service.js b/webclient/components/fileUpload/file-upload-service.js index 398124fcc4..5f01478fd1 100644 --- a/webclient/components/fileUpload/file-upload-service.js +++ b/webclient/components/fileUpload/file-upload-service.js @@ -33,7 +33,7 @@ angular.module('mFileUpload', ['matrixService', 'mUtilities']) console.log("Uploading " + file.name + "... to /matrix/content"); matrixService.uploadContent(file).then( function(response) { - var content_url = location.origin + "/matrix/content/" + response.data.content_token; + var content_url = response.data.content_token; console.log(" -> Successfully uploaded! Available at " + content_url); deferred.resolve(content_url); }, -- cgit 1.5.1 From 74c90f78159e8067b25bfa8a009d2e68419947c8 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 22 Aug 2014 10:50:10 +0100 Subject: Reinitialize room when creating a RoomController so that we start off with a clean slate, as it expects/ --- webclient/components/matrix/event-handler-service.js | 12 +++++++++++- webclient/room/room-controller.js | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 6a01b3fb55..aa8867425e 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -44,6 +44,12 @@ angular.module('eventHandlerService', []) $rootScope.events.rooms[room_id].members = {}; } } + + var reInitRoom = function(room_id) { + $rootScope.events.rooms[room_id] = {}; + $rootScope.events.rooms[room_id].messages = []; + $rootScope.events.rooms[room_id].members = {}; + } var handleMessage = function(event, isLiveEvent) { if ("membership_target" in event.content) { @@ -118,6 +124,10 @@ angular.module('eventHandlerService', []) for (var i=0; i Date: Fri, 22 Aug 2014 10:50:38 +0100 Subject: Keep track of people's presence and query that when we update the members list. --- webclient/components/matrix/event-handler-service.js | 3 +++ webclient/room/room-controller.js | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index aa8867425e..b5eb73d92b 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -35,6 +35,8 @@ angular.module('eventHandlerService', []) $rootScope.events = { rooms: {}, // will contain roomId: { messages:[], members:{userid1: event} } }; + + $rootScope.presence = {}; var initRoom = function(room_id) { if (!(room_id in $rootScope.events.rooms)) { @@ -91,6 +93,7 @@ angular.module('eventHandlerService', []) }; var handlePresence = function(event, isLiveEvent) { + $rootScope.presence[event.content.user_id] = event; $rootScope.$broadcast(PRESENCE_EVENT, event, isLiveEvent); }; diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index a0485e84e8..e204a27e04 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -15,8 +15,8 @@ limitations under the License. */ angular.module('RoomController', ['ngSanitize', 'mUtilities']) -.controller('RoomController', ['$scope', '$http', '$timeout', '$routeParams', '$location', 'matrixService', 'eventStreamService', 'eventHandlerService', 'mFileUpload', 'mUtilities', - function($scope, $http, $timeout, $routeParams, $location, matrixService, eventStreamService, eventHandlerService, mFileUpload, mUtilities) { +.controller('RoomController', ['$scope', '$http', '$timeout', '$routeParams', '$location', 'matrixService', 'eventStreamService', 'eventHandlerService', 'mFileUpload', 'mUtilities', '$rootScope', + function($scope, $http, $timeout, $routeParams, $location, matrixService, eventStreamService, eventHandlerService, mFileUpload, mUtilities, $rootScope) { 'use strict'; var MESSAGES_PER_PAGINATION = 30; var THUMBNAIL_SIZE = 320; @@ -199,6 +199,10 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) ); }); */ + + if (chunk.target_user_id in $rootScope.presence) { + updatePresence($rootScope.presence[chunk.target_user_id]); + } } else { // selectively update membership else it will nuke the picture and displayname too :/ @@ -265,7 +269,7 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities']) $scope.onInit = function() { console.log("onInit"); - + // Does the room ID provided in the URL? var room_id_or_alias; if ($routeParams.room_id_or_alias) { -- cgit 1.5.1