summary refs log tree commit diff
path: root/webclient
diff options
context:
space:
mode:
authorMatthew Hodgson <matthew@matrix.org>2014-08-31 00:40:42 +0100
committerMatthew Hodgson <matthew@matrix.org>2014-08-31 00:40:42 +0100
commit1bc036a12d6877f78f1f5033603d803ac01a13d2 (patch)
tree00408a44816b73132c2aa640346733c3264eb47b /webclient
parentfactor out mobile css into its own file (diff)
downloadsynapse-1bc036a12d6877f78f1f5033603d803ac01a13d2.tar.xz
nasty big monolithic commit of a whole bunch of UI/UX improvements:
 - add a simple CSS template across the app for navigation & cosmetics
 - split login into login & register, and totally reskin it
 - restructure room CSS to play nicely with it
 - implement basis 1:1 chat from user pages
 - disable autofocus on iOS to improve UX
Diffstat (limited to 'webclient')
-rw-r--r--webclient/app-controller.js12
-rw-r--r--webclient/app-directive.js7
-rw-r--r--webclient/app.css296
-rw-r--r--webclient/app.js10
-rw-r--r--webclient/components/matrix/matrix-service.js14
-rw-r--r--webclient/home/home-controller.js4
-rw-r--r--webclient/home/home.html40
-rw-r--r--webclient/index.html18
-rw-r--r--webclient/login/login-controller.js59
-rw-r--r--webclient/login/login.html88
-rw-r--r--webclient/room/room-controller.js7
-rw-r--r--webclient/room/room.html30
-rw-r--r--webclient/settings/settings.html17
-rw-r--r--webclient/user/user-controller.js28
-rw-r--r--webclient/user/user.html36
15 files changed, 356 insertions, 310 deletions
diff --git a/webclient/app-controller.js b/webclient/app-controller.js
index 775113bc87..77a7ad03a5 100644
--- a/webclient/app-controller.js
+++ b/webclient/app-controller.js
@@ -37,6 +37,8 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even
         mPresence.start();
     }
     
+    $scope.user_id = matrixService.config().user_id;
+    
     /**
      * Open a given page.
      * @param {String} url url of the page
@@ -45,6 +47,16 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even
         $location.url(url);
     };
     
+    // Open the given user profile page
+    $scope.goToUserPage = function(user_id) {
+        if (user_id === $scope.user_id) {
+            $location.url("/settings");
+        }
+        else {
+            $location.url("/user/" + user_id);
+        }
+    };
+    
     // Logs the user out 
     $scope.logout = function() {
         
diff --git a/webclient/app-directive.js b/webclient/app-directive.js
index 01f60fdadf..eee0d3842f 100644
--- a/webclient/app-directive.js
+++ b/webclient/app-directive.js
@@ -32,7 +32,12 @@ angular.module('matrixWebClient')
 .directive('ngFocus', ['$timeout', function($timeout) {
     return {
         link: function(scope, element, attr) {
-            $timeout(function() { element[0].focus(); }, 0);
+            // XXX: slightly evil hack to disable autofocus on iOS, as in general
+            // it causes more problems than it fixes, by bouncing the page
+            // around
+            if (!/(iPad|iPhone|iPod)/g.test(navigator.userAgent)) {
+                $timeout(function() { element[0].focus(); }, 0);
+            }            
         }
     };
 }]);
\ No newline at end of file
diff --git a/webclient/app.css b/webclient/app.css
index a5d0199bab..142e764ef1 100644
--- a/webclient/app.css
+++ b/webclient/app.css
@@ -1,122 +1,195 @@
-/*** Mobile voodoo ***/
-@media all and (max-device-width: 640px) {
-            
-    #messageTableWrapper {
-        margin-right: 0px ! important;
-    }
-    
-    .leftBlock {
-        width: 8em ! important;
-        font-size: 8px ! important;
-    }
-    
-    .rightBlock {
-        width: 0px ! important;
-        display: none ! important;
-    }
-    
-    .avatar {
-        width: 36px ! important;
-    }
-    
-    #header,
-    #messageTable,
-    #wrapper,
-    #roomName,
-    #controls {
-        max-width: 640px ! important;
-    }    
-    
-    #userIdCell,
-    #roomRecentsTableWrapper,
-    #usersTableWrapper,
-    #extraControls {
-        display: none;
-    }
-    
-    #buttonsCell {
-        width: 60px ! important;
-        padding-left: 20px ! important;
-    }
-    
-    #roomLogo {
-        display: none;
-    }
-    
-    #roomName {
-        text-align: left ! important;
-        top: -35px ! important;
-    }
-    
-    .bubble {
-        font-size: 12px ! important;
-        min-height: 20px ! important;
-    }
-    
-    #page {
-        top: 35px ! important;
-        bottom: 70px ! important;
-    }
-    
-    #header,
-    #page {
-        margin: 5px ! important;
-    }
-    
-    #header {
-        padding: 5px ! important;
-    }
-        
-    /* stop zoom on select */
-    select:focus,
-    textarea,
-    input
-    {
-        font-size: 16px ! important;
-    }
-    
+/** Common layout **/
+
+html {
+    height: 100%;
 }
 
 body {
+    height: 100%;
     font-family: "Myriad Pro", "Myriad", Helvetica, Arial, sans-serif;
     font-size: 12pt;
     margin: 0px;
 }
 
 h1 {
-    font-family: Helvetica, Arial, sans-serif;
+    font-size: 20pt;
 }
 
-/*** Overall page layout ***/
+a:link    { color: #666; }
+a:visited { color: #666; }
+a:hover   { color: #000; }
+a:active  { color: #000; }
 
 #page {
-    position: absolute;
-    top: 80px;
-    bottom: 100px;
-    left: 0px;
-    right: 0px;
-    margin: 20px;
+    min-height: 100%;
+    margin-bottom: -32px; /* to make room for the footer */
 }
 
 #wrapper {
     margin: auto;
     max-width: 1280px;
     height: 100%;
+    padding-top: 40px;
+    padding-bottom: 40px;
+    padding-left: 20px;
+    padding-right: 20px;
 }
 
-#roomName {
+#header
+{
+    position: absolute;
+    top: 0px;
+    width: 100%;
+    background-color: #333;
+    height: 32px;
+}
+
+#headerContent {
+    color: #ccc;
     max-width: 1280px;
+    margin: auto;
+    text-align: right;
+    height: 32px;
+    line-height: 32px;
+}
+
+#headerContent a:link,
+#headerContent a:visited,
+#headerContent a:hover,
+#headerContent a:active {
+    color: #fff;
+}
+
+#footer
+{
     width: 100%;
+    border-top: #666 1px solid;
+    background-color: #aaa;
+    height: 32px;
+}
+
+#footerContent
+{
+    font-size: 8pt;
+    color: #fff;
+    max-width: 1280px;
+    margin: auto;
+    text-align: center;
+    height: 32px;
+    line-height: 32px;
+}
+
+#genericHeading
+{
+    margin-top: 13px;
+}
+
+#feedback {
+    color: #800;
+}
+
+.mouse-pointer {
+    cursor: pointer;
+}
+
+.invited {
+    opacity: 0.2;
+}
+
+/*** Login Pages ***/
+
+.loginWrapper {
+    text-align: center;
+}
+
+#loginForm {
+    text-align: left;
+    padding: 1em;
+    margin-bottom: 40px;
+    display: inline-block;
+    
+    -webkit-border-radius: 10px;
+    -moz-border-radius: 10px;
+    border-radius: 10px;
+    
+    -webkit-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
+    -moz-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
+    box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
+    
+    background-color: #f8f8f8;
+    border: 1px #ccc solid;
+}
+
+#loginForm input[type='radio'] {
+    margin-right: 1em;
+}
+
+#serverConfig {
+    text-align: center;
+}
+
+#serverConfig,
+#serverConfig input,
+#serverConfig button
+{
+    font-size: 10pt ! important;
+}
+
+.smallPrint {
+    color: #888;
+    font-size: 9pt ! important;
+    font-style: italic ! important;
+}
+
+#serverConfig label {
+    display: inline-block;
     text-align: right;
-    top: -40px;
+    margin-right: 0.5em;
+    width: 7em;   
+}
+
+#loginForm,
+#loginForm input,
+#loginForm button,
+#loginForm select {
+    font-size: 18px;
+}
+
+/*** Room page ***/
+
+#roomPage {
     position: absolute;
+    top: 120px;
+    bottom: 120px;
+    left: 20px;
+    right: 20px;
+}
+
+#roomWrapper {
+    margin: auto;
+    max-width: 1280px;
+    height: 100%;
+}
+
+#roomName {
+    float: right;
     font-size: 16px;
+    margin-top: 15px;
+}
+
+#roomHeader {
+    margin: auto;
+    padding-left: 20px;
+    padding-right: 20px;
+    padding-top: 53px;
+    max-width: 1280px;
 }
 
 #controlPanel {
     position: absolute;
     bottom: 0px;
     width: 100%;
+    height: 100px;
     background-color: #f8f8f8;
     border-top: #aaa 1px solid;
 }
@@ -147,10 +220,6 @@ h1 {
     background-color: #faa;
 }
 
-.mouse-pointer {
-    cursor: pointer;
-}
-
 /*** Participant list ***/
 
 #usersTableWrapper {
@@ -409,11 +478,14 @@ h1 {
 }
 
 /*** Recents in the room page ***/
+
 #roomRecentsTableWrapper {
     float: left;
     max-width: 320px;
-    margin-right: 20px;
+    padding-right: 10px;
+    margin-right: 10px;
     height: 100%;
+    border-right: 1px solid #ddd;
     overflow-y: auto;
 }
 
@@ -434,46 +506,8 @@ h1 {
 }
 
 /*** User profile page ***/
+
 #user-displayname {
     font-size: 24px;
 }
-/******************************/
-
-#header
-{
-    padding: 20px;
-    max-width: 1280px;
-    margin: auto;
-}
-
-#logo,
-#roomLogo {
-    max-width: 1280px;
-    margin: auto;
-}
-
-#header-buttons {
-    float: right;
-}
-
-.text_entry_section {
-    position: fixed;
-    bottom: 0;
-    z-index: 100;
-    left: 0;
-    right: 10em;
-    width: 100%;
-    background: #e0e0e0;
-}
-
-.member_invited {
-    color: blue;
-}
 
-.member_joined {
-
-}
-
-.member_left {
-    color: gray;
-}
diff --git a/webclient/app.js b/webclient/app.js
index 02695c3ae6..9663ddf967 100644
--- a/webclient/app.js
+++ b/webclient/app.js
@@ -18,6 +18,7 @@ var matrixWebClient = angular.module('matrixWebClient', [
     'ngRoute',
     'MatrixWebClientController',
     'LoginController',
+    'RegisterController',
     'RoomController',
     'HomeController',
     'RecentsController',
@@ -38,6 +39,10 @@ matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider',
                 templateUrl: 'login/login.html',
                 controller: 'LoginController'
             }).
+            when('/register', {
+                templateUrl: 'login/register.html',
+                controller: 'RegisterController'
+            }).
             when('/room/:room_id_or_alias', {
                 templateUrl: 'room/room.html',
                 controller: 'RoomController'
@@ -84,7 +89,10 @@ matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider',
 matrixWebClient.run(['$location', 'matrixService', function($location, matrixService) {
 
     // If user auth details are not in cache, go to the login page
-    if (!matrixService.isUserLoggedIn()) {
+    if (!matrixService.isUserLoggedIn() &&
+        $location.path() !== "/login" &&
+        $location.path() !== "/register")
+    {
         $location.path("login");
     }
 
diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js
index 8543491dca..746442c280 100644
--- a/webclient/components/matrix/matrix-service.js
+++ b/webclient/components/matrix/matrix-service.js
@@ -95,14 +95,18 @@ angular.module('matrixService', [])
         },
 
         // Create a room
-        create: function(room_id, visibility) {
+        create: function(room_alias, visibility) {
             // The REST path spec
             var path = "/createRoom";
 
-            return doRequest("POST", path, undefined, {
-                visibility: visibility,
-                room_alias_name: room_id
-            });
+            var req = {
+                "visibility": visibility
+            };
+            if (room_alias) {
+                req.room_alias_name = room_alias;
+            }
+            
+            return doRequest("POST", path, undefined, req);
         },
 
         // List all rooms joined or been invited to
diff --git a/webclient/home/home-controller.js b/webclient/home/home-controller.js
index 438ffe1b0e..847918d5dc 100644
--- a/webclient/home/home-controller.js
+++ b/webclient/home/home-controller.js
@@ -58,14 +58,14 @@ angular.module('HomeController', ['matrixService', 'eventHandlerService', 'Recen
         );
     };
     
-    $scope.createNewRoom = function(room_id, isPrivate) {
+    $scope.createNewRoom = function(room_alias, isPrivate) {
         
         var visibility = "public";
         if (isPrivate) {
             visibility = "private";
         }
         
-        matrixService.create(room_id, visibility).then(
+        matrixService.create(room_alias, visibility).then(
             function(response) { 
                 // This room has been created. Refresh the rooms list
                 console.log("Created room " + response.data.room_alias + " with id: "+
diff --git a/webclient/home/home.html b/webclient/home/home.html
index 1b1c21d9d2..8d35eb5157 100644
--- a/webclient/home/home.html
+++ b/webclient/home/home.html
@@ -1,29 +1,24 @@
 <div ng-controller="HomeController" data-ng-init="onInit()">
 
-    <div id="page">
     <div id="wrapper">
-        
+    
+    <div id="genericHeading">
+        <img src="img/logo-small.png" width="100" height="43" alt="[matrix]"/>
+    </div>
+    
+    <h1>Welcome to homeserver {{ config.homeserver }}</h1>
+    
     <div>
-        <form>
-            <table>
-                <tr>
-                    <td>
-                        <div class="profile-avatar">
-                            <img ng-src="{{ (null !== profile.avatarUrl) ? profile.avatarUrl : 'img/default-profile.jpg' }}"/>
-                        </div>
-                    </td>
-                    <td>
-                        <div id="user-ids">
-                            <div id="user-displayname">{{ profile.displayName }}</div>
-                            <div>{{ config.user_id }}</div>
-                        </div>
-                    </td>
-                </tr>
-            </table>
-        </form>
+        <div class="profile-avatar">
+            <img ng-src="{{ (null !== profile.avatarUrl) ? profile.avatarUrl : 'img/default-profile.png' }}"/>
+        </div>
+        <div id="user-ids">
+            <div id="user-displayname">{{ profile.displayName }}</div>
+            <div>{{ config.user_id }}</div>
+        </div>
     </div>
     
-    <h3>Recents</h3>
+    <h3>Recent conversations</h3>
         <div ng-include="'recents/recents.html'"></div>
     <br/>
 
@@ -38,9 +33,9 @@
     
     <div>
         <form>
-            <input size="40" ng-model="newRoom.room_id" ng-enter="createNewRoom(newRoom.room_id, newRoom.private)" placeholder="(e.g. foo_channel)"/>
+            <input size="40" ng-model="newRoom.room_alias" ng-enter="createNewRoom(newRoom.room_alias, newRoom.private)" placeholder="(e.g. foo_channel)"/>
             <input type="checkbox" ng-model="newRoom.private">private
-            <button ng-disabled="!newRoom.room_id" ng-click="createNewRoom(newRoom.room_id, newRoom.private)">Create room</button>    
+            <button ng-disabled="!newRoom.room_alias" ng-click="createNewRoom(newRoom.room_alias, newRoom.private)">Create room</button>    
         </form>
     </div>
     <div>
@@ -54,5 +49,4 @@
     {{ feedback }}
 
     </div>    
-    </div>
 </div>
diff --git a/webclient/index.html b/webclient/index.html
index fdc50a5212..3c31a8a051 100644
--- a/webclient/index.html
+++ b/webclient/index.html
@@ -4,6 +4,8 @@
     <title>[matrix]</title>
         
     <link rel="stylesheet" href="app.css">
+    <link rel="stylesheet" href="mobile.css">
+    
     <link rel="icon" href="favicon.ico">
    
     <meta name="viewport" content="width=device-width">
@@ -19,6 +21,7 @@
     <script src="app-filter.js"></script>
     <script src="home/home-controller.js"></script>
     <script src="login/login-controller.js"></script>
+    <script src="login/register-controller.js"></script>
     <script src="recents/recents-controller.js"></script>
     <script src="recents/recents-filter.js"></script>
     <script src="room/room-controller.js"></script>
@@ -38,16 +41,23 @@
 
 <body>
 
-    <header id="header">
+    <div id="header">
         <!-- Do not show buttons on the login page -->
-        <div id="header-buttons" ng-hide="'/login' == location ">
+        <div id="headerContent" ng-hide="'/login' == location || '/register' == location">
+            <a href id="headerUserId" ng-click='goToUserPage(user_id)'>{{ user_id }}</a>
+            &nbsp;
             <button ng-click='goToPage("/")'>Home</button>
             <button ng-click='goToPage("settings")'>Settings</button>
             <button ng-click="logout()">Log out</button>
         </div>
-    </header>
+    </div>
 
-    <div ng-view></div>
+    <div id="page" ng-view></div>
 
+    <div id="footer" ng-hide="location.indexOf('/room') == 0">
+        <div id="footerContent">
+            &copy 2014 Matrix.org
+        </div>
+    </div>
 </body>
 </html>
diff --git a/webclient/login/login-controller.js b/webclient/login/login-controller.js
index 51f9a3bdf4..2b91926954 100644
--- a/webclient/login/login-controller.js
+++ b/webclient/login/login-controller.js
@@ -10,63 +10,26 @@ angular.module('LoginController', ['matrixService'])
     if ($location.port()) {
         hs_url += ":" + $location.port();
     }
+    var example_domain = $location.host();
     
     $scope.account = {
         homeserver: hs_url,
+        example_domain: example_domain,
         desired_user_name: "",
         user_id: "",
         password: "",
-        identityServer: "",
+        identityServer: "http://matrix.org:8090",
         pwd1: "",
-        pwd2: ""
+        pwd2: "",
     };
-
-    $scope.register = function() {
-
-        // Set the urls
-        matrixService.setConfig({
-            homeserver: $scope.account.homeserver,
-            identityServer: $scope.account.identityServer
-        });
-        
-        if ($scope.account.pwd1 !== $scope.account.pwd2) {
-            $scope.feedback = "Passwords don't match.";
-            return;
-        }
-        else if ($scope.account.pwd1.length < 6) {
-            $scope.feedback = "Password must be at least 6 characters.";
-            return;
-        }
-
-        matrixService.register($scope.account.desired_user_name, $scope.account.pwd1).then(
-            function(response) {
-                $scope.feedback = "Success";
-                // Update the current config 
-                var config = matrixService.config();
-                angular.extend(config, {
-                    access_token: response.data.access_token,
-                    user_id: response.data.user_id
-                });
-                matrixService.setConfig(config);
-
-                // And permanently save it
-                matrixService.saveConfig();
-                eventStreamService.resume();
-                 // Go to the user's rooms list page
-                $location.url("home");
-            },
-            function(error) {
-                if (error.data) {
-                    if (error.data.errcode === "M_USER_IN_USE") {
-                        $scope.feedback = "Username already taken.";
-                    }
-                }
-                else if (error.status === 0) {
-                    $scope.feedback = "Unable to talk to the server.";
-                }
-            });
+    
+    $scope.login_types = [ "email", "mxid" ];
+    $scope.login_type_label = {
+        "email": "Email address",
+        "mxid": "Matrix ID (e.g. @bob:matrix.org or bob)",
     };
-
+    $scope.login_type = 'mxid'; // TODO: remember the user's preferred login_type
+    
     $scope.login = function() {
         matrixService.setConfig({
             homeserver: $scope.account.homeserver,
diff --git a/webclient/login/login.html b/webclient/login/login.html
index 4b2ea60928..8d5a53ebbc 100644
--- a/webclient/login/login.html
+++ b/webclient/login/login.html
@@ -1,55 +1,49 @@
 <div ng-controller="LoginController" class="login">    
-    <h1 id="logo">[matrix]</h1>
-
-    <div id="page">
-    <div id="wrapper">
-
-    {{ feedback }}
+    <div id="wrapper" class="loginWrapper">
         
-    <h3>Register for an account:</h3>
-    <form novalidate>
-        <input id="desired_user_name" size="70" type="text" auto-focus ng-model="account.desired_user_name" placeholder="User name (ex:bob)"/>
-        <br/>
-        <input id="pwd1" size="70" type="password" auto-focus ng-model="account.pwd1" placeholder="Type a password"/>
-        <br/>
-        <input id="pwd2" size="70" type="password" auto-focus ng-model="account.pwd2" placeholder="Re-type your password"/>
+        <a href ng-click="goToPage('/login')">
+        <img src="img/logo.png" width="240" height="102" alt="[matrix]" style="padding: 50px"/>
+        </a>
+    
         <br/>
-        <!-- New user registration -->
-        <div>
-            <br/>
-            <button ng-click="register()" ng-disabled="!account.desired_user_name || !account.homeserver || !account.pwd1 || !account.pwd2 || account.pwd1 !== account.pwd2">Register</button>
-        </div>
-    </form>
 
-    <h3>Got an account?</h3>
-    <form novalidate>
-        <!-- Login with an registered user -->
-        <div>{{ login_error_msg }} </div>
-        <div>
-            <input id="user_id" size="70" type="text" auto-focus ng-model="account.user_id" placeholder="User ID (ex:@bob:localhost or bob)"/>
-            <br />
-            <input id="password" size="70" type="password" ng-model="account.password" placeholder="Password"/><br />
-            <br/>
-            <button ng-click="login()" ng-disabled="!account.user_id || !account.password || !account.homeserver">Login</button>
-        </div>
-       
-    </form>
+        <form id="loginForm" novalidate>
+            <!-- Login with an registered user -->
+            <div>
+                Log in using:<br/>
+                
+                <div ng-repeat="type in login_types">
+                <input type="radio" ng-model="$parent.login_type" value="{{ type }}" id="radio_{{ type }}"/>
+                <label for="radio_{{ type }}">{{ login_type_label[type] }}</label>
+                </div>
+                    
+                <div style="text-align: center">
+                    <br/>
+                    <input id="user_id" size="32" type="text" ng-focus="true" ng-model="account.user_id" placeholder="{{ login_type_label[login_type] }}"/>
+                    <br/>
+                    <input id="password" size="32" type="password" ng-model="account.password" placeholder="Password"/>
+                    <br/><br/>
+                    <button ng-click="login()" ng-disabled="!account.user_id || !account.password || !account.homeserver">Login</button>
+                    <br/><br/>
+                </div>
 
-    <h3>Servers</h3>
-    <form novalidate>
-        <div>
-        Home Server: 
-        <input id="homeserver" size="57" type="text" ng-model="account.homeserver" placeholder="Home server URL (ex: http://localhost:8080)"/>
-        </div>
-        <br />
-        <div>
-        Identity Server: 
-        <input id="identityServer" size="56" type="text" ng-model="account.identityServer" placeholder="Identity server URL (ex: http://localhost:8090)"/>
-        </div>
-        <br />
-    </form>
-    <br/>
-    
+                <div class="feedback">{{ feedback }} {{ login_error_msg }}</div>
+                
+                <div id="serverConfig">
+                    <label for="homeserver">Home Server:</label> 
+                    <input id="homeserver" size="32" type="text" ng-model="account.homeserver" placeholder="URL (e.g. http://matrix.org:8080)"/>
+                    <div class="smallPrint">Your home server stores all your conversation and account data.</div>
+                    <label for="identityServer">Identity Server:</label>
+                    <input id="identityServer" size="32" type="text" ng-model="account.identityServer" placeholder="URL (e.g. http://matrix.org:8090)"/>
+                    <div class="smallPrint">Matrix provides identity servers to track which emails etc. belong to which Matrix IDs.<br/>
+                        Only http://matrix.org:8090 currently exists.</div>
+                    <br/>
+                    <br/>
+                    <a href="#/register" style="padding-right: 3em">Create account</a>
+                    <a href="#/reset_password">Forgotten password?</a>
+                </div>
+            </div>
+        </form>
 
     </div>
     </div>
diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js
index 75adf259cf..c6028f874e 100644
--- a/webclient/room/room-controller.js
+++ b/webclient/room/room-controller.js
@@ -388,18 +388,13 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput'])
         matrixService.invite($scope.room_id, user_id).then(
             function() {
                 console.log("Invited.");
-                $scope.feedback = "Request for invitation succeeds";
+                $scope.feedback = "Invite sent successfully";
             },
             function(reason) {
                 $scope.feedback = "Failure: " + reason;
             });
     };
 
-    // 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 aa7879ea8f..cfe6b99ea6 100644
--- a/webclient/room/room.html
+++ b/webclient/room/room.html
@@ -1,13 +1,15 @@
-<div ng-controller="RoomController" data-ng-init="onInit()" class="room">
-    <h1 id="roomLogo">[matrix]</h1>
+<div ng-controller="RoomController" data-ng-init="onInit()" class="room" style="height: 100%;">
 
-    <div id="page">
-    <div id="wrapper">
-
-    <div id="roomName">
-        {{ room_alias || room_id }}
+    <div id="roomHeader">
+        <img src="img/logo-small.png" width="100" height="43" alt="[matrix]"/>
+        <div id="roomName">
+            {{ room_alias || room_id }}
+        </div>
     </div>
 
+    <div id="roomPage">
+    <div id="roomWrapper">
+        
     <div id="roomRecentsTableWrapper">
         <div ng-include="'recents/recents.html'"></div>
     </div>
@@ -15,16 +17,16 @@
     <div id="usersTableWrapper">
         <table id="usersTable">
             <tr ng-repeat="member in members | orderMembersList">
-                <td class="userAvatar mouse-pointer" ng-click="goToUserPage(member.id)">
+                <td class="userAvatar mouse-pointer" ng-click="$parent.goToUserPage(member.id)" ng-class="member.membership == 'invite' ? 'invited' : ''">
                     <img class="userAvatarImage" 
-                         ng-src="{{member.avatar_url || 'img/default-profile.jpg'}}" 
+                         ng-src="{{member.avatar_url || 'img/default-profile.png'}}" 
                          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' : '')">
+                <td class="userPresence" ng-class="(member.presenceState === 'online' ? 'online' : (member.presenceState === 'unavailable' ? 'unavailable' : '')) + ' ' + (member.membership == 'invite' ? 'invited' : '')">
                     {{ member.mtime_age + (now - member.last_updated) | duration }}<br/>{{ member.mtime_age ? "ago" : "" }}
                 </td>
         </table>
@@ -40,7 +42,7 @@
                     <div class="timestamp">{{ (msg.content.hsob_ts || msg.ts) | date:'MMM d HH:mm' }}</div>
                 </td>
                 <td class="avatar">
-                    <img class="avatarImage" ng-src="{{ members[msg.user_id].avatar_url || 'img/default-profile.jpg' }}" width="32" height="32"
+                    <img class="avatarImage" ng-src="{{ members[msg.user_id].avatar_url || 'img/default-profile.png' }}" width="32" height="32"
                          ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id || msg.user_id === state.user_id"/>
                 </td>
                 <td ng-class="!msg.content.membership ? (msg.content.msgtype === 'm.emote' ? 'emote text' : 'text') : 'membership text'">
@@ -64,7 +66,7 @@
                     </div>
                 </td>
                 <td class="rightBlock">
-                    <img class="avatarImage" ng-src="{{ members[msg.user_id].avatar_url || 'img/default-profile.jpg' }}" width="32" height="32"
+                    <img class="avatarImage" ng-src="{{ members[msg.user_id].avatar_url || 'img/default-profile.png' }}" width="32" height="32"
                          ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id || msg.user_id !== state.user_id"/>
                 </td>
             </tr>
@@ -86,12 +88,12 @@
                     </td>
                     <td id="buttonsCell">
                         <button ng-click="send()">Send</button>
-                        <button m-file-input="imageFileToSend">Image</button>
+                        <button m-file-input="imageFileToSend" class="extraControls">Image</button>
                     </td>
                 </tr>
             </table>
 
-            <div id="extraControls">
+            <div class="extraControls">
                 <span>
                    Invite a user: 
                         <input ng-model="userIDToInvite" size="32" type="text" placeholder="User ID (ex:@user:homeserver)"/>     
diff --git a/webclient/settings/settings.html b/webclient/settings/settings.html
index 75d8e0887c..0ba3fe263a 100644
--- a/webclient/settings/settings.html
+++ b/webclient/settings/settings.html
@@ -1,16 +1,20 @@
 <div ng-controller="SettingsController" class="user" data-ng-init="onInit()">
 
-    <div id="page">
     <div id="wrapper">
-        
-        <h3>Me</h3>
+
+        <div id="genericHeading">
+            <img src="img/logo-small.png" width="100" height="43" alt="[matrix]"/>
+        </div>
+
+        <h1>Settings</h1>
         <div class="section">
             <form>
                 <div class="profile-avatar">
-                    <img ng-src="{{ (null !== profile.avatarUrl) ? profile.avatarUrl : 'img/default-profile.jpg' }}" m-file-input="profile.avatarFile"/>
+                    <img ng-src="{{ (null !== profile.avatarUrl) ? profile.avatarUrl : 'img/default-profile.png' }}" m-file-input="profile.avatarFile"/>
                 </div>
                 <div id="user-ids">
-                    <input size="40" ng-model="profile.displayName" placeholder="Your name"/>            
+                    <input size="40" ng-model="profile.displayName" placeholder="Your display name"/>
+                    <br/>
                     <button ng-disabled="(profile.displayName == profileOnServer.displayName) && (profile.avatarUrl == profileOnServer.avatarUrl)"
                             ng-click="saveProfile()">Save</button>    
                 </div>
@@ -53,7 +57,7 @@
                 To enable it, reset the notification setting for this web site into your browser settings.
             </div>
             <div ng-switch-when="default">
-                <button ng-click="requestNotifications()">Click here to enable them</button>
+                <button ng-click="requestNotifications()" style="font-size: 14pt">Enable desktop notifications</button>
             </div>
             <div ng-switch-default="">
                 Sorry, your browser does not support notifications.
@@ -73,5 +77,4 @@
         {{ feedback }}
 
     </div>    
-    </div>
 </div>
diff --git a/webclient/user/user-controller.js b/webclient/user/user-controller.js
index 620230561c..b5b2d439a2 100644
--- a/webclient/user/user-controller.js
+++ b/webclient/user/user-controller.js
@@ -25,14 +25,42 @@ angular.module('UserController', ['matrixService'])
         avatar_url: undefined
     };
     
+    $scope.user_id = matrixService.config().user_id;
+    
     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;
         }
     );
+    
+    $scope.messageUser = function() {    
+        
+        // FIXME: create a new room every time, for now
+        
+        matrixService.create(null, 'private').then(
+            function(response) { 
+                // This room has been created. Refresh the rooms list
+                var room_id = response.data.room_id;
+                console.log("Created room with id: "+ room_id);
+                
+                matrixService.invite(room_id, $scope.user.id).then(
+                    function() {
+                        $scope.feedback = "Invite sent successfully";
+                        $scope.$parent.goToPage("/room/" + room_id);
+                    },
+                    function(reason) {
+                        $scope.feedback = "Failure: " + JSON.stringify(reason);
+                    });
+            },
+            function(error) {
+                $scope.feedback = "Failure: " + JSON.stringify(error.data);
+            });                
+    };
+    
 }]);
\ No newline at end of file
diff --git a/webclient/user/user.html b/webclient/user/user.html
index 4c91c8a48a..3574403c87 100644
--- a/webclient/user/user.html
+++ b/webclient/user/user.html
@@ -1,31 +1,25 @@
 <div ng-controller="UserController" class="user">
-    <h1 id="logo">[matrix]</h1>
 
-    <div id="page">
     <div id="wrapper">
-        
+
+        <div id="genericHeading">
+            <img src="img/logo-small.png" width="100" height="43" alt="[matrix]"/>
+        </div>
+
+        <h1>{{ user.displayname || user.id }}</h1>
+
         <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 class="profile-avatar">
+                <img ng-src="{{ user.avatar_url || 'img/default-profile.png' }}"/>
+            </div>
+            <div id="user-ids">
+                <div>{{ user.id }}</div>                        
+            </div>
         </div>
 
+        <button ng-hide="user.id == user_id" ng-click="messageUser()" style="font-size: 14pt; margin-top: 40px; margin-bottom: 40px">Start chat</button>
+        <br/>
         {{ feedback }}
 
-    </div>    
     </div>
 </div>