From 4e14e38bd5cad77dfcab1fdfa3d3a19250810413 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 1 Sep 2014 14:27:03 +0100 Subject: [PATCH 1/7] Just show 'Call' for call events. Don't just splat out the content of the event for other events: it's rarely going to be sesnible. --- webclient/recents/recents.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webclient/recents/recents.html b/webclient/recents/recents.html index 3f025a98d8..56fb38b02a 100644 --- a/webclient/recents/recents.html +++ b/webclient/recents/recents.html @@ -51,7 +51,9 @@
- {{ room.lastMsg }} +
+ Call +
From fa6b3490e299ebdb8c24e5858036bbfb67e76c6b Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 1 Sep 2014 15:53:07 +0100 Subject: [PATCH 2/7] Handle call events in the recents view as well to get consistency with what it displays when refreshed. --- webclient/recents/recents-controller.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/webclient/recents/recents-controller.js b/webclient/recents/recents-controller.js index d33d41a922..c9fd022d7f 100644 --- a/webclient/recents/recents-controller.js +++ b/webclient/recents/recents-controller.js @@ -43,6 +43,11 @@ angular.module('RecentsController', ['matrixService', 'eventHandlerService']) $scope.rooms[event.room_id].lastMsg = event; } }); + $scope.$on(eventHandlerService.CALL_EVENT, function(ngEvent, event, isLive) { + if (isLive) { + $scope.rooms[event.room_id].lastMsg = event; + } + }); }; From 401c16559dc42912b2951ddd0cc57734c92c78df Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 1 Sep 2014 15:58:12 +0100 Subject: [PATCH 3/7] Specced out state/non-state events. Start working on the myriad of syncing APIs. --- docs/specification.rst | 107 +++++++++++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 14 deletions(-) diff --git a/docs/specification.rst b/docs/specification.rst index d47705ca0f..9a494a4c0f 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -391,22 +391,101 @@ If all members in a room leave, that room becomes eligible for deletion. Events in a room ---------------- -- Split into state and non-state data -- Explain what they are, semantics, give examples of clobbering / not, use cases (msgs vs room names). - Not too much detail on the actual event contents. -- API to hit. -- Extensibility provided by the API for custom events. Examples. -- How this hooks into ``initialSync``. -- See the "Room Events" section for actual spec on each type. +Room events can be split into two categories: -Syncing a room --------------- -- Single room initial sync. ``rooms//initialSync`` API to hit. Why it might be used (lazy loading) +:State Events: + These are events which replace events that came before it, depending on a set of unique keys. + These keys are the event ``type`` and a ``state_key``. Events with the same set of keys will + be overwritten. Typically, state events are used to store state, hence their name. -Getting grouped state events ----------------------------- -- ``/members`` and ``/messages`` and the events they return. -- ``/state`` and it returns ALL THE THINGS. +:Non-state events: + These are events which cannot be overwritten after sending. The list of events continues + to grow as more events are sent. As this list grows, it becomes necessary to + provide a mechanism for navigating this list. Pagination APIs are used to view the list + of historical non-state events. Typically, non-state events are used to send messages. + +This specification outlines several events, all with the event type prefix ``m.``. However, +applications may wish to add their own type of event, and this can be achieved using the +REST API detailed in the following sections. If new events are added, the event ``type`` +key SHOULD follow the Java package naming convention, e.g. ``com.example.myapp.event``. +This ensures event types are suitably namespaced for each application and reduces the +risk of clashes. + +State events +------------ +State events can be sent by ``PUT`` ing to ``/rooms//state//``. +These events will be overwritten if ````, ```` and ```` all match. +If the state event has no ``state_key``, it can be omitted from the path. These requests +**cannot use transaction IDs** like other ``PUT`` paths because they cannot be differentiated +from the ``state key``. Furthermore, ``POST`` is unsupported on state paths. Valid requests +look like:: + + PUT /rooms/!roomid:domain/state/m.example.event + { "key" : "without a state key" } + + PUT /rooms/!roomid:domain/state/m.another.example.event/foo + { "key" : "with 'foo' as the state key" } + +In contrast, these requests are invalid:: + + POST /rooms/!roomid:domain/state/m.example.event/ + { "key" : "cannot use POST here" } + + PUT /rooms/!roomid:domain/state/m.another.example.event/foo/11 + { "key" : "txnIds are not supported" } + +Care should be taken to avoid setting the wrong ``state key``:: + + PUT /rooms/!roomid:domain/state/m.another.example.event/11 + { "key" : "with '11' as the state key, but was probably intended to be a txnId" } + +The ``state_key`` is often used to store state about individual users, by using the user ID as the +value. For example:: + + PUT /rooms/!roomid:domain/state/m.favorite.animal.event/%40my_user%3Adomain.com + { "animal" : "cat", "reason": "fluffy" } + +In some cases, there may be no need for a ``state_key``, so it can be omitted:: + + PUT /rooms/!roomid:domain/state/m.room.bgd.color + { "color": "red", "hex": "#ff0000" } + +See "Room Events" for the ``m.`` event specification. + +Non-state events +---------------- +Non-state events can be sent by sending a request to ``/rooms//send/``. +These requests *can* use transaction IDs and ``PUT``/``POST`` methods. Non-state events +allow access to historical events and pagination, making it best suited for sending messages. +For example:: + + POST /rooms/!roomid:domain/send/m.custom.example.message + { "text": "Hello world!" } + + PUT /rooms/!roomid:domain/send/m.custom.example.message/11 + { "text": "Goodbye world!" } + +See "Room Events" for the ``m.`` event specification. + +Syncing rooms +------------- +When a client logs in, they may have a list of rooms which they have already joined. These rooms +may also have a list of events associated with them. The purpose of 'syncing' is to present the +current room and event information in a convenient, compact manner. There are two APIs provided: + + - ``/initialSync`` : A global sync which will present room and event information for all rooms + the user has joined. + + - ``/rooms//initialSync`` : A sync scoped to a single room. Presents room and event + information for this room only. + +- TODO: JSON response format for both types +- TODO: when would you use global? when would you use scoped? + +Getting grouped state events for a room +--------------------------------------- +- ``/members`` and ``/messages`` and the event types they return. Spec JSON response format. +- ``/state`` and it returns ALL THE THINGS. Room Events =========== From fd696f124305ce965f24f10f734dc3d5d3ebe2b6 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Mon, 1 Sep 2014 15:58:16 +0100 Subject: [PATCH 4/7] Perform room unit tests with 'local' users actually in the right domain ;) --- synapse/handlers/message.py | 4 ++++ tests/rest/test_rooms.py | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index c8ff34e5f5..f133458b91 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -76,6 +76,10 @@ class MessageHandler(BaseRoomHandler): Raises: SynapseError if something went wrong. """ + # TODO(paul): Why does 'event' not have a 'user' object? + user = self.hs.parse_userid(event.user_id) + assert(user.is_mine) + if stamp_event: event.content["hsob_ts"] = int(self.clock.time_msec()) diff --git a/tests/rest/test_rooms.py b/tests/rest/test_rooms.py index b432cf254e..cdaf948a3b 100644 --- a/tests/rest/test_rooms.py +++ b/tests/rest/test_rooms.py @@ -51,7 +51,7 @@ class RoomPermissionsTestCase(RestTestCase): persistence_service.get_latest_pdus_in_context.return_value = [] hs = HomeServer( - "test", + "red", db_pool=None, http_client=None, datastore=MemoryDataStore(), @@ -398,7 +398,7 @@ class RoomsMemberListTestCase(RestTestCase): persistence_service.get_latest_pdus_in_context.return_value = [] hs = HomeServer( - "test", + "red", db_pool=None, http_client=None, datastore=MemoryDataStore(), @@ -476,7 +476,7 @@ class RoomsCreateTestCase(RestTestCase): persistence_service.get_latest_pdus_in_context.return_value = [] hs = HomeServer( - "test", + "red", db_pool=None, http_client=None, datastore=MemoryDataStore(), @@ -566,7 +566,7 @@ class RoomTopicTestCase(RestTestCase): persistence_service.get_latest_pdus_in_context.return_value = [] hs = HomeServer( - "test", + "red", db_pool=None, http_client=None, datastore=MemoryDataStore(), @@ -669,7 +669,7 @@ class RoomMemberStateTestCase(RestTestCase): persistence_service.get_latest_pdus_in_context.return_value = [] hs = HomeServer( - "test", + "red", db_pool=None, http_client=None, datastore=MemoryDataStore(), @@ -794,7 +794,7 @@ class RoomMessagesTestCase(RestTestCase): persistence_service.get_latest_pdus_in_context.return_value = [] hs = HomeServer( - "test", + "red", db_pool=None, http_client=None, datastore=MemoryDataStore(), From 59d3955db185de7c07e04ccd7d0479a6b44e1096 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Mon, 1 Sep 2014 16:14:07 +0100 Subject: [PATCH 5/7] Have MemoryDataStore's get_rooms_for_user_where_membership_is() return room membership event objects, as per interface contract --- tests/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/utils.py b/tests/utils.py index 37b759febc..ea7d6893c6 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -188,8 +188,9 @@ class MemoryDataStore(object): def get_rooms_for_user_where_membership_is(self, user_id, membership_list): return [ - r for r in self.members - if self.members[r].get(user_id).membership in membership_list + self.members[r].get(user_id) for r in self.members + if user_id in self.members[r] and + self.members[r][user_id].membership in membership_list ] def get_room_events_stream(self, user_id=None, from_key=None, to_key=None, From 1c6ab2d75994bb0b50d7c31a508c4e5a088feb15 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Mon, 1 Sep 2014 16:16:35 +0100 Subject: [PATCH 6/7] Bump a user's presence last_active time every time they send a message to a room --- synapse/handlers/message.py | 4 ++++ synapse/handlers/presence.py | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index f133458b91..4c74ce3eff 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -90,6 +90,10 @@ class MessageHandler(BaseRoomHandler): yield self._on_new_room_event(event, snapshot) + self.hs.get_handlers().presence_handler.bump_presence_active_time( + user + ) + @defer.inlineCallbacks def get_messages(self, user_id=None, room_id=None, pagin_config=None, feedback=False): diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index c1af07133f..f55bea58a5 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -266,6 +266,12 @@ class PresenceHandler(BaseHandler): # we don't have to do this all the time self.changed_presencelike_data(target_user, state) + def bump_presence_active_time(self, user, now=None): + if now is None: + now = self.clock.time_msec() + + self.changed_presencelike_data(user, {"last_active": now}) + def changed_presencelike_data(self, user, state): statuscache = self._get_or_make_usercache(user) From b9172b982f6359debd867557028e9df12bf3fe71 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 1 Sep 2014 16:22:57 +0100 Subject: [PATCH 7/7] missing semicolon --- webclient/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/index.html b/webclient/index.html index 3c31a8a051..bf24e392ac 100644 --- a/webclient/index.html +++ b/webclient/index.html @@ -56,7 +56,7 @@