summary refs log tree commit diff
path: root/webclient
diff options
context:
space:
mode:
authorErik Johnston <erik@matrix.org>2014-08-19 14:48:19 +0100
committerErik Johnston <erik@matrix.org>2014-08-19 14:48:19 +0100
commit347242a5c408d4ceb1880f6b90c8e9c658226dde (patch)
tree12a25b72d8f13a48f99441c249c3c531c79fff66 /webclient
parentFix bug where we sometimes set min_token to None. (diff)
parentProofing (diff)
downloadsynapse-347242a5c408d4ceb1880f6b90c8e9c658226dde.tar.xz
Merge branch 'master' of github.com:matrix-org/synapse into sql_refactor
Conflicts:
	tests/rest/test_presence.py
	tests/rest/test_rooms.py
	tests/utils.py
Diffstat (limited to 'webclient')
-rw-r--r--webclient/app.css10
-rw-r--r--webclient/app.js13
-rw-r--r--webclient/components/fileInput/file-input-directive.js5
-rw-r--r--webclient/components/fileUpload/file-upload-service.js30
-rw-r--r--webclient/components/matrix/matrix-service.js28
-rw-r--r--webclient/index.html1
-rw-r--r--webclient/room/room-controller.js64
-rw-r--r--webclient/room/room.html10
-rw-r--r--webclient/rooms/rooms-controller.js8
-rw-r--r--webclient/rooms/rooms.html4
-rw-r--r--webclient/user/user-controller.js38
-rw-r--r--webclient/user/user.html30
12 files changed, 207 insertions, 34 deletions
diff --git a/webclient/app.css b/webclient/app.css
index b9e7771ca8..869db69cd6 100644
--- a/webclient/app.css
+++ b/webclient/app.css
@@ -89,6 +89,7 @@ h1 {
     height: 100px;
     position: relative;
     background-color: #000;
+    cursor: pointer;
 }
 
 .userAvatar .userAvatarImage {
@@ -251,6 +252,7 @@ h1 {
     height: 160px;
     display:table-cell;
     vertical-align: middle;
+    text-align: center;
 }
 
 .profile-avatar img {
@@ -258,6 +260,14 @@ h1 {
     max-height: 100%;
 }
 
+/*** User profile page ***/
+#user-ids {
+    padding-left: 1em;
+}
+
+#user-displayname {
+    font-size: 16pt;
+}
 /******************************/
 
 #header {
diff --git a/webclient/app.js b/webclient/app.js
index 8d64db92d3..576912be46 100644
--- a/webclient/app.js
+++ b/webclient/app.js
@@ -20,6 +20,7 @@ var matrixWebClient = angular.module('matrixWebClient', [
     'LoginController',
     'RoomController',
     'RoomsController',
+    'UserController',
     'matrixService',
     'eventStreamService',
     'eventHandlerService',
@@ -33,7 +34,13 @@ matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider',
                 templateUrl: 'login/login.html',
                 controller: 'LoginController'
             }).
-            when('/room/:room_id', {
+            when('/room/:room_id_or_alias', {
+                templateUrl: 'room/room.html',
+                controller: 'RoomController'
+            }).
+            when('/room/', {    // room URL with room alias in it (ex: http://127.0.0.1:8000/#/room/#public:localhost:8080) will come here.
+                                // The reason is that 2nd hash key breaks routeProvider parameters cutting so that the URL will not match with 
+                                // the previous '/room/:room_id_or_alias' URL rule
                 templateUrl: 'room/room.html',
                 controller: 'RoomController'
             }).
@@ -41,6 +48,10 @@ matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider',
                 templateUrl: 'rooms/rooms.html',
                 controller: 'RoomsController'
             }).
+            when('/user/:user_matrix_id', {
+                templateUrl: 'user/user.html',
+                controller: 'UserController'
+            }).
             otherwise({
                 redirectTo: '/rooms'
             });
diff --git a/webclient/components/fileInput/file-input-directive.js b/webclient/components/fileInput/file-input-directive.js
index 9b73f877e9..c5e4ae07a8 100644
--- a/webclient/components/fileInput/file-input-directive.js
+++ b/webclient/components/fileInput/file-input-directive.js
@@ -29,7 +29,7 @@ angular.module('mFileInput', [])
         scope: {
             selectedFile: '=mFileInput'
         },
-        
+
         link: function(scope, element, attrs, ctrl) {
             element.bind("click", function() {
                 element.find("input")[0].click();
@@ -38,6 +38,9 @@ angular.module('mFileInput', [])
                     scope.$apply();
                 });
             });
+
+            // Change the mouse icon on mouseover on this element
+            element.css("cursor", "pointer");
       }
     };
 });
\ No newline at end of file
diff --git a/webclient/components/fileUpload/file-upload-service.js b/webclient/components/fileUpload/file-upload-service.js
index 5729d5da48..d620e6a4d0 100644
--- a/webclient/components/fileUpload/file-upload-service.js
+++ b/webclient/components/fileUpload/file-upload-service.js
@@ -16,11 +16,12 @@
 
 'use strict';
 
+// TODO determine if this is really required as a separate service to matrixService.
 /*
  * Upload an HTML5 file to a server
  */
 angular.module('mFileUpload', [])
-.service('mFileUpload', ['$http', '$q', function ($http, $q) {
+.service('mFileUpload', ['matrixService', '$q', function (matrixService, $q) {
         
     /*
      * Upload an HTML5 file to a server and returned a promise
@@ -28,20 +29,19 @@ angular.module('mFileUpload', [])
      */
     this.uploadFile = function(file) {
         var deferred = $q.defer();
-        
-        // @TODO: This service runs with the do_POST hacky implementation of /synapse/demos/webserver.py.
-        // This is temporary until we have a true file upload service
-        console.log("Uploading " + file.name + "...");
-        $http.post(file.name, file)
-        .success(function(data, status, headers, config) {
-            deferred.resolve(location.origin + data.url);
-            console.log("   -> Successfully uploaded! Available at " + location.origin + data.url);
-        }).
-        error(function(data, status, headers, config) {
-            console.log("   -> Failed to upload"  + file.name);
-            deferred.reject();
-        });
+        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;
+                console.log("   -> Successfully uploaded! Available at " + content_url);
+                deferred.resolve(content_url);
+            },
+            function(error) {
+                console.log("   -> Failed to upload "  + file.name);
+                deferred.reject(error);
+            }
+        );
         
         return deferred.promise;
     };
-}]);
\ No newline at end of file
+}]);
diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js
index 3cd0aa674b..828396c866 100644
--- a/webclient/components/matrix/matrix-service.js
+++ b/webclient/components/matrix/matrix-service.js
@@ -54,13 +54,14 @@ angular.module('matrixService', [])
         
         params.access_token = config.access_token;
         
+        if (path.indexOf(prefixPath) !== 0) {
+            path = prefixPath + path;
+        }
+        
         return doBaseRequest(config.homeserver, method, path, params, data, undefined);
     };
 
     var doBaseRequest = function(baseUrl, method, path, params, data, headers) {
-        if (path.indexOf(prefixPath) !== 0) {
-            path = prefixPath + path;
-        }
         return $http({
             method: method,
             url: baseUrl + path,
@@ -165,6 +166,16 @@ angular.module('matrixService', [])
             return doRequest("DELETE", path, undefined, undefined);
         },
 
+        // Retrieves the room ID corresponding to a room alias
+        resolveRoomAlias:function(room_alias) {
+            var path = "/matrix/client/api/v1/ds/room/$room_alias";
+            room_alias = encodeURIComponent(room_alias);
+
+            path = path.replace("$room_alias", room_alias);
+
+            return doRequest("GET", path, undefined, {});
+        },
+
         sendMessage: function(room_id, msg_id, content) {
             // The REST path spec
             var path = "/rooms/$room_id/messages/$from/$msg_id";
@@ -309,6 +320,17 @@ angular.module('matrixService', [])
             return doBaseRequest(config.identityServer, "POST", path, {}, data, headers); 
         },
         
+        uploadContent: function(file) {
+            var path = "/matrix/content";
+            var headers = {
+                "Content-Type": undefined // undefined means angular will figure it out
+            };
+            var params = {
+                access_token: config.access_token
+            };
+            return doBaseRequest(config.homeserver, "POST", path, params, file, headers);
+        },
+        
         // start listening on /events
         getEventStream: function(from, timeout) {
             var path = "/events";
diff --git a/webclient/index.html b/webclient/index.html
index 455eff4a13..51f6ff1f4d 100644
--- a/webclient/index.html
+++ b/webclient/index.html
@@ -16,6 +16,7 @@
     <script src="login/login-controller.js"></script>
     <script src="room/room-controller.js"></script>
     <script src="rooms/rooms-controller.js"></script>
+    <script src="user/user-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>
diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js
index b859f3d7e8..b585e338ed 100644
--- a/webclient/room/room-controller.js
+++ b/webclient/room/room-controller.js
@@ -108,8 +108,11 @@ angular.module('RoomController', ['ngSanitize'])
                                function($scope, $http, $timeout, $routeParams, $location, matrixService, eventStreamService, eventHandlerService) {
    'use strict';
     var MESSAGES_PER_PAGINATION = 30;
-    $scope.room_id = $routeParams.room_id;
-    $scope.room_alias = matrixService.getRoomIdToAliasMapping($scope.room_id);
+
+    // Room ids. Computed and resolved in onInit
+    $scope.room_id = undefined;
+    $scope.room_alias = undefined;
+
     $scope.state = {
         user_id: matrixService.config().user_id,
         events_from: "END", // when to start the event stream from.
@@ -144,7 +147,7 @@ angular.module('RoomController', ['ngSanitize'])
                 if (document.hidden) {
                     var notification = new window.Notification(
                         ($scope.members[event.user_id].displayname || event.user_id) +
-                        " (" + $scope.room_alias + ")",
+                        " (" + ($scope.room_alias || $scope.room_id) + ")", // FIXME: don't leak room_ids here
                     {
                         "body": event.content.body,
                         "icon": $scope.members[event.user_id].avatar_url,
@@ -342,7 +345,57 @@ angular.module('RoomController', ['ngSanitize'])
     $scope.onInit = function() {
         // $timeout(function() { document.getElementById('textInput').focus() }, 0);
         console.log("onInit");
+        
+        // Does the room ID provided in the URL?
+        var room_id_or_alias;
+        if ($routeParams.room_id_or_alias) {
+            room_id_or_alias = decodeURIComponent($routeParams.room_id_or_alias);
+        }
+
+        if (room_id_or_alias && '!' === room_id_or_alias[0]) {
+            // Yes. We can start right now
+            $scope.room_id = room_id_or_alias;
+            $scope.room_alias = matrixService.getRoomIdToAliasMapping($scope.room_id);
+            onInit2();
+        }
+        else {
+            // No. The URL contains the room alias. Get this alias.
+            if (room_id_or_alias) {
+                // The room alias was passed urlencoded, use it as is
+                $scope.room_alias = room_id_or_alias;
+            }
+            else  {
+                // Else get the room alias by hand from the URL
+                // ie: extract #public:localhost:8080 from http://127.0.0.1:8000/#/room/#public:localhost:8080
+                if (3 === location.hash.split("#").length) {
+                    $scope.room_alias = "#" + location.hash.split("#")[2];
+                }
+                else {
+                    // In case of issue, go to the default page
+                    console.log("Error: cannot extract room alias");
+                    $location.path("/");
+                    return;
+                }
+            }
+            
+            // Need a room ID required in Matrix API requests
+            console.log("Resolving alias: " + $scope.room_alias);
+            matrixService.resolveRoomAlias($scope.room_alias).then(function(response) {
+                $scope.room_id = response.data.room_id;
+                console.log("   -> Room ID: " + $scope.room_id);
+
+                // Now, we can start
+                onInit2();
+            },
+            function () {
+                // In case of issue, go to the default page
+                console.log("Error: cannot resolve room alias");
+                $location.path("/");
+            });
+        }
+    };
 
+    var onInit2 = function() {
         // Join the room
         matrixService.join($scope.room_id).then(
             function() {
@@ -380,6 +433,11 @@ angular.module('RoomController', ['ngSanitize'])
             });
     };
 
+    // Open the user profile page
+    $scope.goToUserPage = function(user_id) {
+        $location.url("/user/" + user_id);
+    };
+
     $scope.leaveRoom = function() {
         
         matrixService.leave($scope.room_id).then(
diff --git a/webclient/room/room.html b/webclient/room/room.html
index 106a9dfd15..36bd95c1bb 100644
--- a/webclient/room/room.html
+++ b/webclient/room/room.html
@@ -10,9 +10,13 @@
     <div id="usersTableWrapper">
         <table id="usersTable">
             <tr ng-repeat="member in members | orderMembersList">
-                <td class="userAvatar">
-                    <img class="userAvatarImage" ng-src="{{member.avatar_url || 'img/default-profile.jpg'}}" width="80" height="80"/>
-                    <img class="userAvatarGradient" src="img/gradient.png" width="80" height="24"/>
+                <td class="userAvatar" ng-click="goToUserPage(member.id)">
+                    <img class="userAvatarImage" 
+                         ng-src="{{member.avatar_url || 'img/default-profile.jpg'}}" 
+                         alt="{{ member.displayname || member.id.substr(0, member.id.indexOf(':')) }}"
+                         title="{{ member.id }}"
+                         width="80" height="80"/>
+                    <img class="userAvatarGradient" src="img/gradient.png" title="{{ member.id }}" width="80" height="24"/>
                     <div class="userName">{{ member.displayname || member.id.substr(0, member.id.indexOf(':')) }}<br/>{{ member.displayname ? "" : member.id.substr(member.id.indexOf(':')) }}</div>
                 </td>
                 <td class="userPresence" ng-class="member.presenceState === 'online' ? 'online' : (member.presenceState === 'unavailable' ? 'unavailable' : '')">
diff --git a/webclient/rooms/rooms-controller.js b/webclient/rooms/rooms-controller.js
index da79c23d55..a237b59b4e 100644
--- a/webclient/rooms/rooms-controller.js
+++ b/webclient/rooms/rooms-controller.js
@@ -149,12 +149,8 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload',
     $scope.joinAlias = function(room_alias) {
         matrixService.joinAlias(room_alias).then(
             function(response) {
-                if (response.data.hasOwnProperty("room_id")) {
-                    $location.path("room/" + response.data.room_id);
-                    return;
-                } else {
-                    // TODO (erikj): Do something here?
-                }
+                // Go to this room
+                $location.path("room/" + room_alias);
             },
             function(error) {
                 $scope.feedback = "Can't join room: " + error.data;
diff --git a/webclient/rooms/rooms.html b/webclient/rooms/rooms.html
index 007ad29999..2602209bd3 100644
--- a/webclient/rooms/rooms.html
+++ b/webclient/rooms/rooms.html
@@ -65,7 +65,7 @@
     
     <div class="rooms" ng-repeat="(rm_id, room) in rooms">
         <div>
-            <a href="#/room/{{ rm_id }}" >{{ room.room_alias }}</a> {{room.membership === 'invite' ? ' (invited)' : ''}}
+            <a href="#/room/{{ room.room_alias ? room.room_alias : rm_id }}" >{{ room.room_alias }}</a> {{room.membership === 'invite' ? ' (invited)' : ''}}
         </div>
     </div>
     <br/>
@@ -74,7 +74,7 @@
     
     <div class="public_rooms" ng-repeat="room in public_rooms">
         <div>
-            <a href="#/room/{{ room.room_id }}" >{{ room.room_alias }}</a>
+            <a href="#/room/{{ room.room_alias ? room.room_alias : room.room_id }}" >{{ room.room_alias }}</a>
         </div>
     </div>
     <br/>
diff --git a/webclient/user/user-controller.js b/webclient/user/user-controller.js
new file mode 100644
index 0000000000..620230561c
--- /dev/null
+++ b/webclient/user/user-controller.js
@@ -0,0 +1,38 @@
+/*
+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('UserController', ['matrixService'])
+.controller('UserController', ['$scope', '$routeParams', 'matrixService',
+                              function($scope, $routeParams, matrixService) {                 
+    $scope.user = {
+        id: $routeParams.user_matrix_id,
+        displayname: "",
+        avatar_url: undefined
+    };
+    
+    matrixService.getDisplayName($scope.user.id).then(
+        function(response) {
+            $scope.user.displayname = response.data.displayname;
+        }
+    ); 
+    matrixService.getProfilePictureUrl($scope.user.id).then(
+        function(response) {
+            $scope.user.avatar_url = response.data.avatar_url;
+        }
+    );
+}]);
\ No newline at end of file
diff --git a/webclient/user/user.html b/webclient/user/user.html
new file mode 100644
index 0000000000..47db09d1ee
--- /dev/null
+++ b/webclient/user/user.html
@@ -0,0 +1,30 @@
+<div ng-controller="UserController" class="user">
+
+    <div id="page">
+    <div id="wrapper">
+        
+        <div>
+            <form>
+                <table>
+                    <tr>
+                        <td>
+                            <div class="profile-avatar">
+                                <img ng-src="{{ user.avatar_url || 'img/default-profile.jpg' }}"/>
+                            </div>
+                        </td>
+                        <td>
+                            <div id="user-ids">
+                                <div id="user-displayname">{{ user.displayname }}</div>
+                                <div>{{ user.id }}</div>                        
+                            </div>
+                        </td>
+                    </tr>
+                </table>
+            </form>
+        </div>
+
+        {{ feedback }}
+
+    </div>    
+    </div>
+</div>