diff --git a/docs/client-server/swagger_matrix/rooms b/docs/client-server/swagger_matrix/rooms
index 1ead8b8c14..bb49ec5a6a 100644
--- a/docs/client-server/swagger_matrix/rooms
+++ b/docs/client-server/swagger_matrix/rooms
@@ -14,13 +14,103 @@
},
"apis": [
{
+ "path": "/rooms/{roomId}/send/{eventType}/{txnId}",
+ "operations": [
+ {
+ "method": "PUT",
+ "summary": "Send a generic non-state event to this room.",
+ "notes": "This operation can also be done as a POST to /rooms/{roomId}/send/{eventType}",
+ "type": "EventId",
+ "nickname": "send_non_state_event",
+ "consumes": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "body",
+ "description": "The event contents",
+ "required": true,
+ "type": "EventContent",
+ "paramType": "body"
+ },
+ {
+ "name": "roomId",
+ "description": "The room to send the message in.",
+ "required": true,
+ "type": "string",
+ "paramType": "path"
+ },
+ {
+ "name": "eventType",
+ "description": "The type of event to send.",
+ "required": true,
+ "type": "string",
+ "paramType": "path"
+ },
+ {
+ "name": "txnId",
+ "description": "A client transaction ID to ensure idempotency. This can only be omitted if the HTTP method becomes a POST.",
+ "required": true,
+ "type": "string",
+ "paramType": "path"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "path": "/rooms/{roomId}/state/{eventType}/{stateKey}",
+ "operations": [
+ {
+ "method": "PUT",
+ "summary": "Send a generic state event to this room.",
+ "notes": "The state key can be omitted, such that you can PUT to /rooms/{roomId}/state/{eventType}. The state key defaults to a 0 length string in this case.",
+ "type": "void",
+ "nickname": "send_state_event",
+ "consumes": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "body",
+ "description": "The event contents",
+ "required": true,
+ "type": "EventContent",
+ "paramType": "body"
+ },
+ {
+ "name": "roomId",
+ "description": "The room to send the message in.",
+ "required": true,
+ "type": "string",
+ "paramType": "path"
+ },
+ {
+ "name": "eventType",
+ "description": "The type of event to send.",
+ "required": true,
+ "type": "string",
+ "paramType": "path"
+ },
+ {
+ "name": "stateKey",
+ "description": "An identifier used to specify clobbering semantics. State events with the same (roomId, eventType, stateKey) will be replaced.",
+ "required": true,
+ "type": "string",
+ "paramType": "path"
+ }
+ ]
+ }
+ ]
+ },
+ {
"path": "/rooms/{roomId}/send/m.room.message/{txnId}",
"operations": [
{
"method": "PUT",
"summary": "Send a message in this room.",
- "notes": "Send a message in this room.",
- "type": "void",
+ "notes": "This operation can also be done as a POST to /rooms/{roomId}/send/m.room.message",
+ "type": "EventId",
"nickname": "send_message",
"consumes": [
"application/json"
@@ -42,7 +132,7 @@
},
{
"name": "txnId",
- "description": "A client transaction ID to ensure idempotency.",
+ "description": "A client transaction ID to ensure idempotency. This can only be omitted if the HTTP method becomes a POST.",
"required": true,
"type": "string",
"paramType": "path"
@@ -110,8 +200,8 @@
{
"method": "PUT",
"summary": "Send feedback to a message.",
- "notes": "Send feedback to a message.",
- "type": "void",
+ "notes": "This operation can also be done as a POST to /rooms/{roomId}/send/m.room.message.feedback",
+ "type": "EventId",
"nickname": "send_feedback",
"consumes": [
"application/json"
@@ -133,7 +223,7 @@
},
{
"name": "txnId",
- "description": "A client transaction ID to ensure idempotency.",
+ "description": "A client transaction ID to ensure idempotency. This can only be omitted if the HTTP method becomes a POST.",
"required": true,
"type": "string",
"paramType": "path"
@@ -488,6 +578,51 @@
]
}
]
+ },
+ {
+ "path": "/rooms/{roomId}/state",
+ "operations": [
+ {
+ "method": "GET",
+ "summary": "Get a list of all the current state events for this room.",
+ "notes": "Get a list of all the current state events for this room.",
+ "type": "array",
+ "items": {
+ "$ref": "Event"
+ },
+ "nickname": "get_state_events",
+ "parameters": [
+ {
+ "name": "roomId",
+ "description": "The room to get a list of current state events from.",
+ "required": true,
+ "type": "string",
+ "paramType": "path"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "path": "/rooms/{roomId}/initialSync",
+ "operations": [
+ {
+ "method": "GET",
+ "summary": "Get all the current information for this room, including messages and state events.",
+ "notes": "Get all the current information for this room, including messages and state events.",
+ "type": "InitialSyncRoomData",
+ "nickname": "get_room_sync_data",
+ "parameters": [
+ {
+ "name": "roomId",
+ "description": "The room to get information for.",
+ "required": true,
+ "type": "string",
+ "paramType": "path"
+ }
+ ]
+ }
+ ]
}
],
"models": {
@@ -633,12 +768,17 @@
"properties": {
"event_id": {
"type": "string",
- "description": "An ID which uniquely identifies this event.",
+ "description": "An ID which uniquely identifies this event. This is automatically set by the server.",
"required": true
},
"room_id": {
"type": "string",
- "description": "The room in which this event occurred.",
+ "description": "The room in which this event occurred. This is automatically set by the server.",
+ "required": true
+ },
+ "type": {
+ "type": "string",
+ "description": "The event type.",
"required": true
}
},
@@ -646,6 +786,26 @@
"MessageEvent"
]
},
+ "EventId": {
+ "id": "EventId",
+ "properties": {
+ "event_id": {
+ "type": "string",
+ "description": "The allocated event ID for this event.",
+ "required": true
+ }
+ }
+ },
+ "EventContent": {
+ "id": "EventContent",
+ "properties": {
+ "__event_content_keys__": {
+ "type": "string",
+ "description": "Event-specific content keys and values.",
+ "required": false
+ }
+ }
+ },
"MessageEvent": {
"id": "MessageEvent",
"properties": {
@@ -670,6 +830,34 @@
"description": "The fully-qualified user ID."
}
}
+ },
+ "InitialSyncRoomData": {
+ "id": "InitialSyncRoomData",
+ "properties": {
+ "membership": {
+ "type": "string",
+ "description": "This user's membership state in this room.",
+ "required": true
+ },
+ "room_id": {
+ "type": "string",
+ "description": "The ID of this room.",
+ "required": true
+ },
+ "messages": {
+ "type": "MessagePaginationChunk",
+ "description": "The most recent messages for this room, governed by the limit parameter.",
+ "required": false
+ },
+ "state": {
+ "type": "array",
+ "description": "A list of state events representing the current state of the room.",
+ "required": false,
+ "items": {
+ "$ref": "Event"
+ }
+ }
+ }
}
}
}
diff --git a/synapse/rest/room.py b/synapse/rest/room.py
index 66efaa76f0..a10b3b54f9 100644
--- a/synapse/rest/room.py
+++ b/synapse/rest/room.py
@@ -322,6 +322,50 @@ class RoomMessageListRestServlet(RestServlet):
defer.returnValue((200, msgs))
+# TODO: Needs unit testing
+class RoomStateRestServlet(RestServlet):
+ PATTERN = client_path_pattern("/rooms/(?P<room_id>[^/]*)/state$")
+
+ @defer.inlineCallbacks
+ def on_GET(self, request, room_id):
+ user = yield self.auth.get_user_by_req(request)
+ # TODO: Get all the current state for this room and return in the same
+ # format as initial sync, that is:
+ # [
+ # { state event }, { state event }
+ # ]
+ defer.returnValue((200, []))
+
+
+# TODO: Needs unit testing
+class RoomInitialSyncRestServlet(RestServlet):
+ PATTERN = client_path_pattern("/rooms/(?P<room_id>[^/]*)/initialSync$")
+
+ @defer.inlineCallbacks
+ def on_GET(self, request, room_id):
+ user = yield self.auth.get_user_by_req(request)
+ # TODO: Get all the initial sync data for this room and return in the
+ # same format as initial sync, that is:
+ # {
+ # membership: join,
+ # messages: [
+ # chunk: [ msg events ],
+ # start: s_tok,
+ # end: e_tok
+ # ],
+ # room_id: foo,
+ # state: [
+ # { state event } , { state event }
+ # ]
+ # }
+ # Probably worth keeping the keys room_id and membership for parity with
+ # /initialSync even though they must be joined to sync this and know the
+ # room ID, so clients can reuse the same code (room_id and membership
+ # are MANDATORY for /initialSync, so the code will expect it to be
+ # there)
+ defer.returnValue((200, {}))
+
+
class RoomTriggerBackfill(RestServlet):
PATTERN = client_path_pattern("/rooms/(?P<room_id>[^/]*)/backfill$")
@@ -436,3 +480,5 @@ def register_servlets(hs, http_server):
RoomMembershipRestServlet(hs).register(http_server)
RoomSendEventRestServlet(hs).register(http_server)
PublicRoomListRestServlet(hs).register(http_server)
+ RoomStateRestServlet(hs).register(http_server)
+ RoomInitialSyncRestServlet(hs).register(http_server)
diff --git a/webclient/app.css b/webclient/app.css
index bc23f76f00..16f9dd72b7 100644
--- a/webclient/app.css
+++ b/webclient/app.css
@@ -7,6 +7,16 @@
.leftBlock {
width: 8em ! important;
+ font-size: 8px ! important;
+ }
+
+ .rightBlock {
+ width: 0px ! important;
+ display: none ! important;
+ }
+
+ .avatar {
+ width: 36px ! important;
}
#header,
@@ -368,6 +378,10 @@ h1 {
background-color: #f8f8ff;
}
+.recentsRoomSelected {
+ background-color: #eee;
+}
+
.recentsRoomName {
font-size: 16px;
padding-top: 7px;
@@ -387,6 +401,15 @@ h1 {
padding-bottom: 5px;
}
+/*** Recents in the room page ***/
+#roomRecentsTableWrapper {
+ float: left;
+ max-width: 320px;
+ margin-right: 20px;
+ height: 100%;
+ overflow-y: auto;
+}
+
/*** Profile ***/
.profile-avatar {
diff --git a/webclient/recents/recents-controller.js b/webclient/recents/recents-controller.js
index a9805fc38a..8f8b08d5bd 100644
--- a/webclient/recents/recents-controller.js
+++ b/webclient/recents/recents-controller.js
@@ -20,7 +20,11 @@ angular.module('RecentsController', ['matrixService', 'eventHandlerService'])
.controller('RecentsController', ['$scope', 'matrixService', 'eventHandlerService', 'eventStreamService',
function($scope, matrixService, eventHandlerService, eventStreamService) {
$scope.rooms = {};
-
+
+ // $scope of the parent where the recents component is included can override this value
+ // in order to highlight a specific room in the list
+ $scope.recentsSelectedRoomID;
+
$scope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) {
var config = matrixService.config();
if (event.state_key === config.user_id && event.content.membership === "invite") {
diff --git a/webclient/recents/recents.html b/webclient/recents/recents.html
index 6d2864ac97..6fda6c5c6b 100644
--- a/webclient/recents/recents.html
+++ b/webclient/recents/recents.html
@@ -1,6 +1,9 @@
<div ng-controller="RecentsController" data-ng-init="onInit()">
<table class="recentsTable">
- <tbody ng-repeat="(rm_id, room) in rooms | orderRecents" ng-click="goToPage('room/' + (room.room_alias ? room.room_alias : room.room_id) )" class ="recentsRoom" >
+ <tbody ng-repeat="(rm_id, room) in rooms | orderRecents"
+ ng-click="goToPage('room/' + (room.room_alias ? room.room_alias : room.room_id) )"
+ class ="recentsRoom"
+ ng-class="{'recentsRoomSelected': (room.room_id === recentsSelectedRoomID)}">
<tr>
<td class="recentsRoomName">
{{ room.room_display_name }}
diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js
index f49deaa489..6c98db269e 100644
--- a/webclient/room/room-controller.js
+++ b/webclient/room/room-controller.js
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-angular.module('RoomController', ['ngSanitize', 'mUtilities'])
+angular.module('RoomController', ['ngSanitize', 'mFileInput', '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';
@@ -327,6 +327,9 @@ angular.module('RoomController', ['ngSanitize', 'mUtilities'])
var onInit2 = function() {
eventHandlerService.reInitRoom($scope.room_id);
+ // Make recents highlight the current room
+ $scope.recentsSelectedRoomID = $scope.room_id;
+
// Join the room
matrixService.join($scope.room_id).then(
function() {
diff --git a/webclient/room/room.html b/webclient/room/room.html
index c167819f15..236ca0a89b 100644
--- a/webclient/room/room.html
+++ b/webclient/room/room.html
@@ -7,7 +7,11 @@
<div id="roomName">
{{ room_alias || room_id }}
</div>
-
+
+ <div id="roomRecentsTableWrapper">
+ <div ng-include="'recents/recents.html'"></div>
+ </div>
+
<div id="usersTableWrapper">
<table id="usersTable">
<tr ng-repeat="member in members | orderMembersList">
diff --git a/webclient/settings/settings-controller.js b/webclient/settings/settings-controller.js
index 5d3f7cb2b8..f7d5e8eb75 100644
--- a/webclient/settings/settings-controller.js
+++ b/webclient/settings/settings-controller.js
@@ -16,7 +16,7 @@ limitations under the License.
'use strict';
-angular.module('SettingsController', ['matrixService', 'mFileUpload'])
+angular.module('SettingsController', ['matrixService', 'mFileUpload', 'mFileInput'])
.controller('SettingsController', ['$scope', 'matrixService', 'mFileUpload',
function($scope, matrixService, mFileUpload) {
$scope.config = matrixService.config();
|