diff options
Diffstat (limited to '')
-rw-r--r-- | synapse/rest/__init__.py | 4 | ||||
-rw-r--r-- | synapse/rest/directory.py | 2 | ||||
-rw-r--r-- | synapse/rest/public.py | 33 | ||||
-rw-r--r-- | synapse/rest/room.py | 12 | ||||
-rw-r--r-- | webclient/app-controller.js | 6 | ||||
-rw-r--r-- | webclient/app.css | 45 | ||||
-rw-r--r-- | webclient/app.js | 1 | ||||
-rw-r--r-- | webclient/components/matrix/matrix-service.js | 51 | ||||
-rw-r--r-- | webclient/home/home-controller.js | 67 | ||||
-rw-r--r-- | webclient/home/home.html | 9 | ||||
-rw-r--r-- | webclient/index.html | 4 | ||||
-rw-r--r-- | webclient/recents/recents-controller.js | 66 | ||||
-rw-r--r-- | webclient/recents/recents-filter.js | 47 | ||||
-rw-r--r-- | webclient/recents/recents.html | 53 |
14 files changed, 289 insertions, 111 deletions
diff --git a/synapse/rest/__init__.py b/synapse/rest/__init__.py index 47896612ce..f33024e72a 100644 --- a/synapse/rest/__init__.py +++ b/synapse/rest/__init__.py @@ -15,8 +15,7 @@ from . import ( - room, events, register, login, profile, public, presence, initial_sync, - directory + room, events, register, login, profile, presence, initial_sync, directory ) @@ -40,7 +39,6 @@ class RestServletFactory(object): register.register_servlets(hs, client_resource) login.register_servlets(hs, client_resource) profile.register_servlets(hs, client_resource) - public.register_servlets(hs, client_resource) presence.register_servlets(hs, client_resource) initial_sync.register_servlets(hs, client_resource) directory.register_servlets(hs, client_resource) diff --git a/synapse/rest/directory.py b/synapse/rest/directory.py index be9a3f5f9f..dc347652a0 100644 --- a/synapse/rest/directory.py +++ b/synapse/rest/directory.py @@ -31,7 +31,7 @@ def register_servlets(hs, http_server): class ClientDirectoryServer(RestServlet): - PATTERN = client_path_pattern("/ds/room/(?P<room_alias>[^/]*)$") + PATTERN = client_path_pattern("/directory/room/(?P<room_alias>[^/]*)$") @defer.inlineCallbacks def on_GET(self, request, room_alias): diff --git a/synapse/rest/public.py b/synapse/rest/public.py deleted file mode 100644 index 3430c8049f..0000000000 --- a/synapse/rest/public.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -# 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. - -"""This module contains REST servlets to do with public paths: /public""" -from twisted.internet import defer - -from base import RestServlet, client_path_pattern - - -class PublicRoomListRestServlet(RestServlet): - PATTERN = client_path_pattern("/public/rooms$") - - @defer.inlineCallbacks - def on_GET(self, request): - handler = self.handlers.room_list_handler - data = yield handler.get_public_room_list() - defer.returnValue((200, data)) - - -def register_servlets(hs, http_server): - PublicRoomListRestServlet(hs).register(http_server) diff --git a/synapse/rest/room.py b/synapse/rest/room.py index f4c12191c8..66efaa76f0 100644 --- a/synapse/rest/room.py +++ b/synapse/rest/room.py @@ -265,6 +265,17 @@ class JoinRoomAliasServlet(RestServlet): # TODO: Needs unit testing +class PublicRoomListRestServlet(RestServlet): + PATTERN = client_path_pattern("/publicRooms$") + + @defer.inlineCallbacks + def on_GET(self, request): + handler = self.handlers.room_list_handler + data = yield handler.get_public_room_list() + defer.returnValue((200, data)) + + +# TODO: Needs unit testing class RoomMemberListRestServlet(RestServlet): PATTERN = client_path_pattern("/rooms/(?P<room_id>[^/]*)/members$") @@ -424,3 +435,4 @@ def register_servlets(hs, http_server): RoomTriggerBackfill(hs).register(http_server) RoomMembershipRestServlet(hs).register(http_server) RoomSendEventRestServlet(hs).register(http_server) + PublicRoomListRestServlet(hs).register(http_server) diff --git a/webclient/app-controller.js b/webclient/app-controller.js index f210119e21..5d3fa6ddc8 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -37,7 +37,11 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even mPresence.start(); } - $scope.go = function(url) { + /** + * Open a given page. + * @param {String} url url of the page + */ + $scope.goToPage = function(url) { $location.url(url); }; diff --git a/webclient/app.css b/webclient/app.css index dfa17fae62..bc23f76f00 100644 --- a/webclient/app.css +++ b/webclient/app.css @@ -342,6 +342,51 @@ h1 { top: 0; } +/*** Recents ***/ +.recentsTable { + max-width: 480px; + width: 100%; + border-collapse: collapse; + table-layout: fixed; +} + +.recentsTable tr { + width: 100%; +} +.recentsTable td { + vertical-align: text-top; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.recentsRoom { + cursor: pointer; +} + +.recentsRoom:hover { + background-color: #f8f8ff; +} + +.recentsRoomName { + font-size: 16px; + padding-top: 7px; + width: auto; +} + +.recentsRoomSummaryTS { + color: #888; + font-size: 12px; + width: 7em; + text-align: right; +} + +.recentsRoomSummary { + color: #888; + font-size: 12px; + padding-bottom: 5px; +} + /*** Profile ***/ .profile-avatar { diff --git a/webclient/app.js b/webclient/app.js index 6cd50c5e54..1d5503ebc0 100644 --- a/webclient/app.js +++ b/webclient/app.js @@ -20,6 +20,7 @@ var matrixWebClient = angular.module('matrixWebClient', [ 'LoginController', 'RoomController', 'HomeController', + 'RecentsController', 'SettingsController', 'UserController', 'matrixService', diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index e467ca40da..2feddac5d8 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -106,11 +106,20 @@ angular.module('matrixService', []) }, // List all rooms joined or been invited to - rooms: function(from, to, limit) { + rooms: function(limit, feedback) { // The REST path spec + var path = "/initialSync"; - return doRequest("GET", path); + var params = {}; + if (limit) { + params.limit = limit; + } + if (feedback) { + params.feedback = feedback; + } + + return doRequest("GET", path, params); }, // Joins a room @@ -155,7 +164,7 @@ angular.module('matrixService', []) // Retrieves the room ID corresponding to a room alias resolveRoomAlias:function(room_alias) { - var path = "/matrix/client/api/v1/ds/room/$room_alias"; + var path = "/matrix/client/api/v1/directory/room/$room_alias"; room_alias = encodeURIComponent(room_alias); path = path.replace("$room_alias", room_alias); @@ -235,7 +244,7 @@ angular.module('matrixService', []) // get a list of public rooms on your home server publicRooms: function() { - var path = "/public/rooms" + var path = "/publicRooms" return doRequest("GET", path); }, @@ -406,6 +415,40 @@ angular.module('matrixService', []) config.version = configVersion; localStorage.setItem("config", JSON.stringify(config)); }, + + + /****** Room aliases management ******/ + + /** + * Enhance data returned by rooms() and publicRooms() by adding room_alias + * & room_display_name which are computed from data already retrieved from the server. + * @param {Array} data the response of rooms() and publicRooms() + * @returns {Array} the same array with enriched objects + */ + assignRoomAliases: function(data) { + for (var i=0; i<data.length; i++) { + var alias = this.getRoomIdToAliasMapping(data[i].room_id); + if (alias) { + // use the existing alias from storage + data[i].room_alias = alias; + data[i].room_display_name = alias; + } + else if (data[i].aliases && data[i].aliases[0]) { + // save the mapping + // TODO: select the smarter alias from the array + this.createRoomIdToAliasMapping(data[i].room_id, data[i].aliases[0]); + data[i].room_display_name = data[i].aliases[0]; + } + else if (data[i].membership == "invite" && "inviter" in data[i]) { + data[i].room_display_name = data[i].inviter + "'s room" + } + else { + // last resort use the room id + data[i].room_display_name = data[i].room_id; + } + } + return data; + }, createRoomIdToAliasMapping: function(roomId, alias) { localStorage.setItem(MAPPING_PREFIX+roomId, alias); diff --git a/webclient/home/home-controller.js b/webclient/home/home-controller.js index 867ae522a6..e8e91eede7 100644 --- a/webclient/home/home-controller.js +++ b/webclient/home/home-controller.js @@ -16,12 +16,11 @@ limitations under the License. 'use strict'; -angular.module('HomeController', ['matrixService', 'mFileInput', 'mFileUpload', 'eventHandlerService']) -.controller('HomeController', ['$scope', '$location', 'matrixService', 'mFileUpload', 'eventHandlerService', 'eventStreamService', - function($scope, $location, matrixService, mFileUpload, eventHandlerService, eventStreamService) { +angular.module('HomeController', ['matrixService', 'eventHandlerService', 'RecentsController']) +.controller('HomeController', ['$scope', '$location', 'matrixService', 'eventHandlerService', 'eventStreamService', + function($scope, $location, matrixService, eventHandlerService, eventStreamService) { $scope.config = matrixService.config(); - $scope.rooms = {}; $scope.public_rooms = []; $scope.newRoomId = ""; $scope.feedback = ""; @@ -32,72 +31,18 @@ angular.module('HomeController', ['matrixService', 'mFileInput', 'mFileUpload', }; $scope.goToRoom = { - room_id: "", + room_id: "" }; $scope.joinAlias = { - room_alias: "", - }; - - $scope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) { - var config = matrixService.config(); - if (event.state_key === config.user_id && event.content.membership === "invite") { - console.log("Invited to room " + event.room_id); - // FIXME push membership to top level key to match /im/sync - event.membership = event.content.membership; - // FIXME bodge a nicer name than the room ID for this invite. - event.room_display_name = event.user_id + "'s room"; - $scope.rooms[event.room_id] = event; - } - }); - - var assignRoomAliases = function(data) { - for (var i=0; i<data.length; i++) { - var alias = matrixService.getRoomIdToAliasMapping(data[i].room_id); - if (alias) { - // use the existing alias from storage - data[i].room_alias = alias; - data[i].room_display_name = alias; - } - else if (data[i].aliases && data[i].aliases[0]) { - // save the mapping - // TODO: select the smarter alias from the array - matrixService.createRoomIdToAliasMapping(data[i].room_id, data[i].aliases[0]); - data[i].room_display_name = data[i].aliases[0]; - } - else if (data[i].membership == "invite" && "inviter" in data[i]) { - data[i].room_display_name = data[i].inviter + "'s room" - } - else { - // last resort use the room id - data[i].room_display_name = data[i].room_id; - } - } - return data; + room_alias: "" }; var refresh = function() { - // List all rooms joined or been invited to - matrixService.rooms(1,true).then( - function(response) { - var data = assignRoomAliases(response.data.rooms); - $scope.feedback = "Success"; - for (var i=0; i<data.length; i++) { - $scope.rooms[data[i].room_id] = data[i]; - } - - var presence = response.data.presence; - for (var i = 0; i < presence.length; ++i) { - eventHandlerService.handleEvent(presence[i], false); - } - }, - function(error) { - $scope.feedback = "Failure: " + error.data; - }); matrixService.publicRooms().then( function(response) { - $scope.public_rooms = assignRoomAliases(response.data.chunk); + $scope.public_rooms = matrixService.assignRoomAliases(response.data.chunk); } ); diff --git a/webclient/home/home.html b/webclient/home/home.html index 4084f4c388..d38b843d83 100644 --- a/webclient/home/home.html +++ b/webclient/home/home.html @@ -23,13 +23,8 @@ </form> </div> - <h3>My rooms</h3> - - <div class="rooms" ng-repeat="(rm_id, room) in rooms"> - <div> - <a href="#/room/{{ room.room_alias ? room.room_alias : rm_id }}" >{{ room.room_display_name }}</a> {{room.membership === 'invite' ? ' (invited)' : ''}} - </div> - </div> + <h3>Recents</h3> + <div ng-include="'recents/recents.html'"></div> <br/> <h3>Public rooms</h3> diff --git a/webclient/index.html b/webclient/index.html index 6031036e9a..16f0e8ac5f 100644 --- a/webclient/index.html +++ b/webclient/index.html @@ -19,6 +19,8 @@ <script src="app-filter.js"></script> <script src="home/home-controller.js"></script> <script src="login/login-controller.js"></script> + <script src="recents/recents-controller.js"></script> + <script src="recents/recents-filter.js"></script> <script src="room/room-controller.js"></script> <script src="room/room-directive.js"></script> <script src="settings/settings-controller.js"></script> @@ -37,7 +39,7 @@ <header id="header"> <!-- Do not show buttons on the login page --> <div id="header-buttons" ng-hide="'/login' == location "> - <button ng-click='go("settings")'>Settings</button> + <button ng-click='goToPage("settings")'>Settings</button> <button ng-click="logout()">Log out</button> </div> </header> diff --git a/webclient/recents/recents-controller.js b/webclient/recents/recents-controller.js new file mode 100644 index 0000000000..a9805fc38a --- /dev/null +++ b/webclient/recents/recents-controller.js @@ -0,0 +1,66 @@ +/* + 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'; + +angular.module('RecentsController', ['matrixService', 'eventHandlerService']) +.controller('RecentsController', ['$scope', 'matrixService', 'eventHandlerService', 'eventStreamService', + function($scope, matrixService, eventHandlerService, eventStreamService) { + $scope.rooms = {}; + + $scope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) { + var config = matrixService.config(); + if (event.state_key === config.user_id && event.content.membership === "invite") { + console.log("Invited to room " + event.room_id); + // FIXME push membership to top level key to match /im/sync + event.membership = event.content.membership; + // FIXME bodge a nicer name than the room ID for this invite. + event.room_display_name = event.user_id + "'s room"; + $scope.rooms[event.room_id] = event; + } + }); + + var refresh = function() { + // List all rooms joined or been invited to + matrixService.rooms(1, false).then( + function(response) { + var data = matrixService.assignRoomAliases(response.data.rooms); + for (var i=0; i<data.length; i++) { + $scope.rooms[data[i].room_id] = data[i]; + + // Create a shortcut for the last message of this room + if (data[i].messages && data[i].messages.chunk && data[i].messages.chunk[0]) { + $scope.rooms[data[i].room_id].lastMsg = data[i].messages.chunk[0]; + } + } + + var presence = response.data.presence; + for (var i = 0; i < presence.length; ++i) { + eventHandlerService.handleEvent(presence[i], false); + } + }, + function(error) { + $scope.feedback = "Failure: " + error.data; + } + ); + }; + + $scope.onInit = function() { + refresh(); + }; + +}]); + diff --git a/webclient/recents/recents-filter.js b/webclient/recents/recents-filter.js new file mode 100644 index 0000000000..45653fca96 --- /dev/null +++ b/webclient/recents/recents-filter.js @@ -0,0 +1,47 @@ +/* + 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'; + +angular.module('RecentsController') +.filter('orderRecents', function() { + return function(rooms) { + + // Transform the dict into an array + // The key, room_id, is already in value objects + var filtered = []; + angular.forEach(rooms, function(value, key) { + filtered.push( value ); + }); + + // And time sort them + // The room with the lastest message at first + filtered.sort(function (a, b) { + // Invite message does not have a body message nor ts + // Puth them at the top of the list + if (undefined === a.lastMsg) { + return -1; + } + else if (undefined === b.lastMsg) { + return 1; + } + else { + return b.lastMsg.ts - a.lastMsg.ts; + } + }); + return filtered; + }; +}); \ No newline at end of file diff --git a/webclient/recents/recents.html b/webclient/recents/recents.html new file mode 100644 index 0000000000..6d2864ac97 --- /dev/null +++ b/webclient/recents/recents.html @@ -0,0 +1,53 @@ +<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" > + <tr> + <td class="recentsRoomName"> + {{ room.room_display_name }} + </td> + <td class="recentsRoomSummaryTS"> + {{ (room.lastMsg.ts) | date:'MMM d HH:mm' }} + </td> + </tr> + + <tr> + <td colspan="2" class="recentsRoomSummary"> + + <div ng-show="room.membership === 'invite'" > + {{ room.inviter }} invited you + </div> + + <div ng-hide="room.membership === 'invite'" ng-switch="room.lastMsg.type" > + <div ng-switch-when="m.room.member"> + {{ room.lastMsg.user_id }} + {{ {"join": "joined", "leave": "left", "invite": "invited"}[room.lastMsg.content.membership] }} + {{ room.lastMsg.content.membership === "invite" ? (room.lastMsg.state_key || '') : '' }} + </div> + + <div ng-switch-when="m.room.message"> + <div ng-switch="room.lastMsg.content.msgtype"> + <div ng-switch-when="m.text"> + {{ room.lastMsg.user_id }} : + <span ng-bind-html="(room.lastMsg.content.body) | linky:'_blank'"> + </span> + </div> + + <div ng-switch-when="m.image"> + {{ room.lastMsg.user_id }} sent an image + </div> + + <div ng-switch-default> + {{ room.lastMsg.content }} + </div> + </div> + </div> + + <div ng-switch-default> + {{ room.lastMsg }} + </div> + </div> + </td> + </tr> + </tbody> + </table> +</div> |