diff --git a/webclient/app-controller.js b/webclient/app-controller.js
index 41055bdcd2..086fa3d946 100644
--- a/webclient/app-controller.js
+++ b/webclient/app-controller.js
@@ -55,8 +55,14 @@ angular.module('MatrixWebClientController', ['matrixService'])
// And go to the login page
$location.path("login");
- };
-
+ };
+
+ // Listen to the event indicating that the access token is no more valid.
+ // In this case, the user needs to log in again.
+ $scope.$on("M_UNKNOWN_TOKEN", function() {
+ console.log("Invalid access token -> log user out");
+ $scope.logout();
+ });
}]);
\ No newline at end of file
diff --git a/webclient/app.css b/webclient/app.css
index 65049c95c9..122f25c9ff 100644
--- a/webclient/app.css
+++ b/webclient/app.css
@@ -219,6 +219,20 @@ h1 {
background-color: #fff ! important;
}
+/*** Profile ***/
+
+.profile-avatar {
+ width: 160px;
+ height: 160px;
+ display:table-cell;
+ vertical-align: middle;
+}
+
+.profile-avatar img {
+ max-width: 100%;
+ max-height: 100%;
+}
+
/******************************/
.header {
diff --git a/webclient/app.js b/webclient/app.js
index 651aeeaa77..0b613fa206 100644
--- a/webclient/app.js
+++ b/webclient/app.js
@@ -23,8 +23,8 @@ var matrixWebClient = angular.module('matrixWebClient', [
'matrixService'
]);
-matrixWebClient.config(['$routeProvider',
- function($routeProvider) {
+matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider',
+ function($routeProvider, $provide, $httpProvider) {
$routeProvider.
when('/login', {
templateUrl: 'login/login.html',
@@ -41,6 +41,22 @@ matrixWebClient.config(['$routeProvider',
otherwise({
redirectTo: '/rooms'
});
+
+ $provide.factory('AccessTokenInterceptor', ['$q', '$rootScope',
+ function ($q, $rootScope) {
+ return {
+ responseError: function(rejection) {
+ if (rejection.status === 403 && "data" in rejection &&
+ "errcode" in rejection.data &&
+ rejection.data.errcode === "M_UNKNOWN_TOKEN") {
+ console.log("Got a 403 with an unknown token. Logging out.")
+ $rootScope.$broadcast("M_UNKNOWN_TOKEN");
+ }
+ return $q.reject(rejection);
+ }
+ };
+ }]);
+ $httpProvider.interceptors.push('AccessTokenInterceptor');
}]);
matrixWebClient.run(['$location', 'matrixService' , function($location, matrixService) {
@@ -75,4 +91,4 @@ matrixWebClient
return function(text) {
return $sce.trustAsHtml(text);
};
- }]);
\ No newline at end of file
+ }]);
diff --git a/webclient/components/fileInput/file-input-directive.js b/webclient/components/fileInput/file-input-directive.js
new file mode 100644
index 0000000000..9b73f877e9
--- /dev/null
+++ b/webclient/components/fileInput/file-input-directive.js
@@ -0,0 +1,43 @@
+/*
+ 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';
+
+/*
+ * Transform an element into an image file input button.
+ * Watch to the passed variable change. It will contain the selected HTML5 file object.
+ */
+angular.module('mFileInput', [])
+.directive('mFileInput', function() {
+ return {
+ restrict: 'A',
+ transclude: 'true',
+ template: '<div ng-transclude></div><input ng-hide="true" type="file" accept="image/*"/>',
+ scope: {
+ selectedFile: '=mFileInput'
+ },
+
+ link: function(scope, element, attrs, ctrl) {
+ element.bind("click", function() {
+ element.find("input")[0].click();
+ element.find("input").bind("change", function(e) {
+ scope.selectedFile = this.files[0];
+ scope.$apply();
+ });
+ });
+ }
+ };
+});
\ No newline at end of file
diff --git a/webclient/components/fileUpload/file-upload-service.js b/webclient/components/fileUpload/file-upload-service.js
new file mode 100644
index 0000000000..5729d5da48
--- /dev/null
+++ b/webclient/components/fileUpload/file-upload-service.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';
+
+/*
+ * Upload an HTML5 file to a server
+ */
+angular.module('mFileUpload', [])
+.service('mFileUpload', ['$http', '$q', function ($http, $q) {
+
+ /*
+ * Upload an HTML5 file to a server and returned a promise
+ * that will provide the URL of the uploaded file.
+ */
+ 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();
+ });
+
+ 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 f054bf301e..6d66111469 100644
--- a/webclient/components/matrix/matrix-service.js
+++ b/webclient/components/matrix/matrix-service.js
@@ -17,7 +17,7 @@ limitations under the License.
'use strict';
angular.module('matrixService', [])
-.factory('matrixService', ['$http', '$q', function($http, $q) {
+.factory('matrixService', ['$http', '$q', '$rootScope', function($http, $q, $rootScope) {
/*
* Permanent storage of user information
@@ -49,28 +49,13 @@ angular.module('matrixService', [])
if (path.indexOf(prefixPath) !== 0) {
path = prefixPath + path;
}
- // Do not directly return the $http instance but return a promise
- // with enriched or cleaned information
- var deferred = $q.defer();
- $http({
+ return $http({
method: method,
url: baseUrl + path,
params: params,
data: data,
headers: headers
})
- .success(function(data, status, headers, config) {
- // @TODO: We could detect a bad access token here and make an automatic logout
- deferred.resolve(data, status, headers, config);
- })
- .error(function(data, status, headers, config) {
- // Enrich the error callback with an human readable error reason
- var reason = data.error;
- if (!data.error) {
- reason = JSON.stringify(data);
- }
- deferred.reject(reason, data, status, headers, config);
- });
return deferred.promise;
};
@@ -227,6 +212,17 @@ angular.module('matrixService', [])
path = path.replace("$room_id", room_id);
return doRequest("GET", path);
},
+
+ paginateBackMessages: function(room_id, from_token, limit) {
+ var path = "/rooms/$room_id/messages/list";
+ path = path.replace("$room_id", room_id);
+ var params = {
+ from: from_token,
+ to: "START",
+ limit: limit
+ };
+ return doRequest("GET", path, params);
+ },
// get a list of public rooms on your home server
publicRooms: function() {
@@ -301,6 +297,12 @@ angular.module('matrixService', [])
return doBaseRequest(config.identityServer, "POST", path, {}, data, headers);
},
+
+ //
+ testLogin: function() {
+
+ },
+
/****** Permanent storage of user information ******/
// Returns the current config
diff --git a/webclient/index.html b/webclient/index.html
index ddc9ab5e32..e62ec39669 100644
--- a/webclient/index.html
+++ b/webclient/index.html
@@ -14,6 +14,8 @@
<script src="room/room-controller.js"></script>
<script src="rooms/rooms-controller.js"></script>
<script src="components/matrix/matrix-service.js"></script>
+ <script src="components/fileInput/file-input-directive.js"></script>
+ <script src="components/fileUpload/file-upload-service.js"></script>
</head>
<body>
diff --git a/webclient/login/login-controller.js b/webclient/login/login-controller.js
index 26590da686..8bd6a4e84f 100644
--- a/webclient/login/login-controller.js
+++ b/webclient/login/login-controller.js
@@ -3,8 +3,16 @@ angular.module('LoginController', ['matrixService'])
function($scope, $location, matrixService) {
'use strict';
+
+ // Assume that this is hosted on the home server, in which case the URL
+ // contains the home server.
+ var hs_url = $location.protocol() + "://" + $location.host();
+ if ($location.port()) {
+ hs_url += ":" + $location.port();
+ }
+
$scope.account = {
- homeserver: "http://localhost:8080",
+ homeserver: hs_url,
desired_user_name: "",
user_id: "",
password: "",
@@ -31,14 +39,13 @@ angular.module('LoginController', ['matrixService'])
}
matrixService.register($scope.account.desired_user_name, $scope.account.pwd1).then(
- function(data) {
+ function(response) {
$scope.feedback = "Success";
-
// Update the current config
var config = matrixService.config();
angular.extend(config, {
- access_token: data.access_token,
- user_id: data.user_id
+ access_token: response.data.access_token,
+ user_id: response.data.user_id
});
matrixService.setConfig(config);
@@ -48,8 +55,15 @@ angular.module('LoginController', ['matrixService'])
// Go to the user's rooms list page
$location.path("rooms");
},
- function(reason) {
- $scope.feedback = "Failure: " + reason;
+ 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.";
+ }
});
};
@@ -61,18 +75,28 @@ angular.module('LoginController', ['matrixService'])
// try to login
matrixService.login($scope.account.user_id, $scope.account.password).then(
function(response) {
- if ("access_token" in response) {
+ if ("access_token" in response.data) {
$scope.feedback = "Login successful.";
matrixService.setConfig({
homeserver: $scope.account.homeserver,
- user_id: $scope.account.user_id,
- access_token: response.access_token
+ user_id: response.data.user_id,
+ access_token: response.data.access_token
});
matrixService.saveConfig();
$location.path("rooms");
}
else {
- $scope.feedback = "Failed to login: " + JSON.stringify(response);
+ $scope.feedback = "Failed to login: " + JSON.stringify(response.data);
+ }
+ },
+ function(error) {
+ if (error.data) {
+ if (error.data.errcode === "M_FORBIDDEN") {
+ $scope.login_error_msg = "Incorrect username or password.";
+ }
+ }
+ else if (error.status === 0) {
+ $scope.login_error_msg = "Unable to talk to the server.";
}
}
);
diff --git a/webclient/login/login.html b/webclient/login/login.html
index 508ff5e4bf..a8b2b1f12d 100644
--- a/webclient/login/login.html
+++ b/webclient/login/login.html
@@ -15,15 +15,16 @@
<!-- New user registration -->
<div>
<br/>
- <button ng-click="register()" ng-disabled="!account.desired_user_name || !account.homeserver || !account.identityServer || !account.pwd1 || !account.pwd2">Register</button>
+ <button ng-click="register()" ng-disabled="!account.desired_user_name || !account.homeserver || !account.identityServer || !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)"/>
+ <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/>
diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js
index 470f41521a..fb6e2025fc 100644
--- a/webclient/room/room-controller.js
+++ b/webclient/room/room-controller.js
@@ -18,11 +18,15 @@ angular.module('RoomController', [])
.controller('RoomController', ['$scope', '$http', '$timeout', '$routeParams', '$location', 'matrixService',
function($scope, $http, $timeout, $routeParams, $location, matrixService) {
'use strict';
+ var MESSAGES_PER_PAGINATION = 10;
$scope.room_id = $routeParams.room_id;
$scope.room_alias = matrixService.getRoomIdToAliasMapping($scope.room_id);
$scope.state = {
user_id: matrixService.config().user_id,
- events_from: "START"
+ events_from: "END", // when to start the event stream from.
+ earliest_token: "END", // stores how far back we've paginated.
+ can_paginate: true, // this is toggled off when we run out of items
+ stream_failure: undefined // the response when the stream fails
};
$scope.messages = [];
$scope.members = {};
@@ -30,6 +34,53 @@ angular.module('RoomController', [])
$scope.imageURLToSend = "";
$scope.userIDToInvite = "";
+
+ var scrollToBottom = function() {
+ $timeout(function() {
+ var objDiv = document.getElementsByClassName("messageTableWrapper")[0];
+ objDiv.scrollTop = objDiv.scrollHeight;
+ },0);
+ };
+
+ var parseChunk = function(chunks, appendToStart) {
+ for (var i = 0; i < chunks.length; i++) {
+ var chunk = chunks[i];
+ if (chunk.room_id == $scope.room_id && chunk.type == "m.room.message") {
+ if ("membership_target" in chunk.content) {
+ chunk.user_id = chunk.content.membership_target;
+ }
+ if (appendToStart) {
+ $scope.messages.unshift(chunk);
+ }
+ else {
+ $scope.messages.push(chunk);
+ scrollToBottom();
+ }
+ }
+ else if (chunk.room_id == $scope.room_id && chunk.type == "m.room.member") {
+ updateMemberList(chunk);
+ }
+ else if (chunk.type === "m.presence") {
+ updatePresence(chunk);
+ }
+ }
+ };
+
+ var paginate = function(numItems) {
+ matrixService.paginateBackMessages($scope.room_id, $scope.state.earliest_token, numItems).then(
+ function(response) {
+ parseChunk(response.data.chunk, true);
+ $scope.state.earliest_token = response.data.end;
+ if (response.data.chunk.length < MESSAGES_PER_PAGINATION) {
+ // no more messages to paginate :(
+ $scope.state.can_paginate = false;
+ }
+ },
+ function(error) {
+ console.log("Failed to paginateBackMessages: " + JSON.stringify(error));
+ }
+ )
+ };
var shortPoll = function() {
$http.get(matrixService.config().homeserver + matrixService.prefix + "/events", {
@@ -39,30 +90,13 @@ angular.module('RoomController', [])
"timeout": 5000
}})
.then(function(response) {
+ $scope.state.stream_failure = undefined;
console.log("Got response from "+$scope.state.events_from+" to "+response.data.end);
$scope.state.events_from = response.data.end;
-
$scope.feedback = "";
- for (var i = 0; i < response.data.chunk.length; i++) {
- var chunk = response.data.chunk[i];
- if (chunk.room_id == $scope.room_id && chunk.type == "m.room.message") {
- if ("membership_target" in chunk.content) {
- chunk.user_id = chunk.content.membership_target;
- }
- $scope.messages.push(chunk);
- $timeout(function() {
- var objDiv = document.getElementsByClassName("messageTableWrapper")[0];
- objDiv.scrollTop = objDiv.scrollHeight;
- },0);
- }
- else if (chunk.room_id == $scope.room_id && chunk.type == "m.room.member") {
- updateMemberList(chunk);
- }
- else if (chunk.type === "m.presence") {
- updatePresence(chunk);
- }
- }
+ parseChunk(response.data.chunk, false);
+
if ($scope.stopPoll) {
console.log("Stopping polling.");
}
@@ -70,7 +104,7 @@ angular.module('RoomController', [])
$timeout(shortPoll, 0);
}
}, function(response) {
- $scope.feedback = "Can't stream: " + response.data;
+ $scope.state.stream_failure = response;
if (response.status == 403) {
$scope.stopPoll = true;
@@ -99,8 +133,8 @@ angular.module('RoomController', [])
function(response) {
var member = $scope.members[chunk.target_user_id];
if (member !== undefined) {
- console.log("Updated displayname "+chunk.target_user_id+" to " + response.displayname);
- member.displayname = response.displayname;
+ console.log("Updated displayname "+chunk.target_user_id+" to " + response.data.displayname);
+ member.displayname = response.data.displayname;
}
}
);
@@ -108,8 +142,8 @@ angular.module('RoomController', [])
function(response) {
var member = $scope.members[chunk.target_user_id];
if (member !== undefined) {
- console.log("Updated image for "+chunk.target_user_id+" to " + response.avatar_url);
- member.avatar_url = response.avatar_url;
+ console.log("Updated image for "+chunk.target_user_id+" to " + response.data.avatar_url);
+ member.avatar_url = response.data.avatar_url;
}
}
);
@@ -171,8 +205,8 @@ angular.module('RoomController', [])
console.log("Sent message");
$scope.textInput = "";
},
- function(reason) {
- $scope.feedback = "Failed to send: " + reason;
+ function(error) {
+ $scope.feedback = "Failed to send: " + error.data.error;
});
};
@@ -183,22 +217,24 @@ angular.module('RoomController', [])
// Join the room
matrixService.join($scope.room_id).then(
function() {
- console.log("Joined room");
+ console.log("Joined room "+$scope.room_id);
// Now start reading from the stream
$timeout(shortPoll, 0);
// Get the current member list
matrixService.getMemberList($scope.room_id).then(
function(response) {
- for (var i = 0; i < response.chunk.length; i++) {
- var chunk = response.chunk[i];
+ for (var i = 0; i < response.data.chunk.length; i++) {
+ var chunk = response.data.chunk[i];
updateMemberList(chunk);
}
},
- function(reason) {
- $scope.feedback = "Failed get member list: " + reason;
+ function(error) {
+ $scope.feedback = "Failed get member list: " + error.data.error;
}
);
+
+ paginate(MESSAGES_PER_PAGINATION);
},
function(reason) {
$scope.feedback = "Can't join room: " + reason;
@@ -224,8 +260,8 @@ angular.module('RoomController', [])
console.log("Left room ");
$location.path("rooms");
},
- function(reason) {
- $scope.feedback = "Failed to leave room: " + reason;
+ function(error) {
+ $scope.feedback = "Failed to leave room: " + error.data.error;
});
};
@@ -234,10 +270,14 @@ angular.module('RoomController', [])
function() {
console.log("Image sent");
},
- function(reason) {
- $scope.feedback = "Failed to send image: " + reason;
+ function(error) {
+ $scope.feedback = "Failed to send image: " + error.data.error;
});
};
+
+ $scope.loadMoreHistory = function() {
+ paginate(MESSAGES_PER_PAGINATION);
+ };
$scope.$on('$destroy', function(e) {
console.log("onDestroyed: Stopping poll.");
diff --git a/webclient/room/room.html b/webclient/room/room.html
index 8fc7d5d360..3b9ba713de 100644
--- a/webclient/room/room.html
+++ b/webclient/room/room.html
@@ -35,7 +35,7 @@
<div class="bubble">
{{ msg.content.msgtype === "m.emote" ? ("* " + (members[msg.user_id].displayname || msg.user_id) + " " + msg.content.body) : "" }}
{{ msg.content.msgtype === "m.text" ? msg.content.body : "" }}
- <img class="image" ng-hide='msg.content.msgtype !== "m.image"' src="{{ msg.content.url }}" alt="{{ msg.content.body }}"/>
+ <img class="image" ng-hide='msg.content.msgtype !== "m.image"' ng-src="{{ msg.content.url }}" alt="{{ msg.content.body }}"/>
</div>
</td>
<td class="rightBlock">
@@ -86,6 +86,10 @@
<button ng-click="inviteUser(userIDToInvite)">Invite</button>
</span>
<button ng-click="leaveRoom()">Leave</button>
+ <button ng-click="loadMoreHistory()" ng-disabled="!state.can_paginate">Load more history</button>
+ <div ng-hide="!state.stream_failure">
+ {{ state.stream_failure.data.error || "Connection failure" }}
+ </div>
</div>
</div>
diff --git a/webclient/rooms/rooms-controller.js b/webclient/rooms/rooms-controller.js
index 293ea8bc8b..2ce14e1d49 100644
--- a/webclient/rooms/rooms-controller.js
+++ b/webclient/rooms/rooms-controller.js
@@ -16,9 +16,9 @@ limitations under the License.
'use strict';
-angular.module('RoomsController', ['matrixService'])
-.controller('RoomsController', ['$scope', '$location', 'matrixService',
- function($scope, $location, matrixService) {
+angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload'])
+.controller('RoomsController', ['$scope', '$location', 'matrixService', 'mFileUpload',
+ function($scope, $location, matrixService, mFileUpload) {
$scope.rooms = [];
$scope.public_rooms = [];
@@ -40,7 +40,8 @@ angular.module('RoomsController', ['matrixService'])
$scope.newProfileInfo = {
name: matrixService.config().displayName,
- avatar: matrixService.config().avatarUrl
+ avatar: matrixService.config().avatarUrl,
+ avatarFile: undefined
};
$scope.linkedEmails = {
@@ -74,18 +75,18 @@ angular.module('RoomsController', ['matrixService'])
// List all rooms joined or been invited to
$scope.rooms = matrixService.rooms();
matrixService.rooms().then(
- function(data) {
- data = assignRoomAliases(data);
+ function(response) {
+ var data = assignRoomAliases(response.data);
$scope.feedback = "Success";
$scope.rooms = data;
},
- function(reason) {
- $scope.feedback = "Failure: " + reason;
+ function(error) {
+ $scope.feedback = "Failure: " + error.data;
});
matrixService.publicRooms().then(
- function(data) {
- $scope.public_rooms = assignRoomAliases(data.chunk);
+ function(response) {
+ $scope.public_rooms = assignRoomAliases(response.data.chunk);
}
);
};
@@ -100,14 +101,14 @@ angular.module('RoomsController', ['matrixService'])
matrixService.create(room_id, visibility).then(
function(response) {
// This room has been created. Refresh the rooms list
- console.log("Created room " + response.room_alias + " with id: "+
- response.room_id);
+ console.log("Created room " + response.data.room_alias + " with id: "+
+ response.data.room_id);
matrixService.createRoomIdToAliasMapping(
- response.room_id, response.room_alias);
+ response.data.room_id, response.data.room_alias);
$scope.refresh();
},
- function(reason) {
- $scope.feedback = "Failure: " + reason;
+ function(error) {
+ $scope.feedback = "Failure: " + error.data;
});
};
@@ -117,17 +118,17 @@ angular.module('RoomsController', ['matrixService'])
//$location.path("room/" + room_id);
matrixService.join(room_id).then(
function(response) {
- if (response.hasOwnProperty("room_id")) {
- if (response.room_id != room_id) {
- $location.path("room/" + response.room_id);
+ if (response.data.hasOwnProperty("room_id")) {
+ if (response.data.room_id != room_id) {
+ $location.path("room/" + response.data.room_id);
return;
}
}
$location.path("room/" + room_id);
},
- function(reason) {
- $scope.feedback = "Can't join room: " + reason;
+ function(error) {
+ $scope.feedback = "Can't join room: " + error.data;
}
);
};
@@ -135,15 +136,15 @@ angular.module('RoomsController', ['matrixService'])
$scope.joinAlias = function(room_alias) {
matrixService.joinAlias(room_alias).then(
function(response) {
- if (response.hasOwnProperty("room_id")) {
- $location.path("room/" + response.room_id);
+ if (response.data.hasOwnProperty("room_id")) {
+ $location.path("room/" + response.data.room_id);
return;
} else {
// TODO (erikj): Do something here?
}
},
- function(reason) {
- $scope.feedback = "Can't join room: " + reason;
+ function(error) {
+ $scope.feedback = "Can't join room: " + error.data;
}
);
};
@@ -157,12 +158,28 @@ angular.module('RoomsController', ['matrixService'])
matrixService.setConfig(config);
matrixService.saveConfig();
},
- function(reason) {
- $scope.feedback = "Can't update display name: " + reason;
+ function(error) {
+ $scope.feedback = "Can't update display name: " + error.data;
}
);
};
+
+ $scope.$watch("newProfileInfo.avatarFile", function(newValue, oldValue) {
+ if ($scope.newProfileInfo.avatarFile) {
+ console.log("Uploading new avatar file...");
+ mFileUpload.uploadFile($scope.newProfileInfo.avatarFile).then(
+ function(url) {
+ $scope.newProfileInfo.avatar = url;
+ $scope.setAvatar($scope.newProfileInfo.avatar);
+ },
+ function(error) {
+ $scope.feedback = "Can't upload image";
+ }
+ );
+ }
+ });
+
$scope.setAvatar = function(newUrl) {
console.log("Updating avatar to "+newUrl);
matrixService.setProfilePictureUrl(newUrl).then(
@@ -174,8 +191,8 @@ angular.module('RoomsController', ['matrixService'])
matrixService.setConfig(config);
matrixService.saveConfig();
},
- function(reason) {
- $scope.feedback = "Can't update avatar: " + reason;
+ function(error) {
+ $scope.feedback = "Can't update avatar: " + error.data;
}
);
};
@@ -183,8 +200,8 @@ angular.module('RoomsController', ['matrixService'])
$scope.linkEmail = function(email) {
matrixService.linkEmail(email).then(
function(response) {
- if (response.success === true) {
- $scope.linkedEmails.authTokenId = response.tokenId;
+ if (response.data.success === true) {
+ $scope.linkedEmails.authTokenId = response.data.tokenId;
$scope.emailFeedback = "You have been sent an email.";
$scope.linkedEmails.emailBeingAuthed = email;
}
@@ -192,8 +209,8 @@ angular.module('RoomsController', ['matrixService'])
$scope.emailFeedback = "Failed to send email.";
}
},
- function(reason) {
- $scope.emailFeedback = "Can't send email: " + reason;
+ function(error) {
+ $scope.emailFeedback = "Can't send email: " + error.data;
}
);
};
@@ -206,7 +223,7 @@ angular.module('RoomsController', ['matrixService'])
}
matrixService.authEmail(matrixService.config().user_id, tokenId, code).then(
function(response) {
- if ("success" in response && response.success === false) {
+ if ("success" in response.data && response.data.success === false) {
$scope.emailFeedback = "Failed to authenticate email.";
return;
}
diff --git a/webclient/rooms/rooms.html b/webclient/rooms/rooms.html
index d303e143b9..5974bd940c 100644
--- a/webclient/rooms/rooms.html
+++ b/webclient/rooms/rooms.html
@@ -5,16 +5,33 @@
<div>
<form>
- <input size="40" ng-model="newProfileInfo.name" ng-enter="setDisplayName(newProfileInfo.name)" />
- <button ng-disabled="!newProfileInfo.name" ng-click="setDisplayName(newProfileInfo.name)">Update Name</button>
+ <table>
+ <tr>
+ <td>
+ <div class="profile-avatar">
+ <img ng-src="{{ newProfileInfo.avatar || 'img/default-profile.jpg' }}" m-file-input="newProfileInfo.avatarFile"/>
+ </div>
+ </td>
+ <td>
+ <button m-file-input="newProfileInfo.avatarFile">Upload new Avatar</button>
+ or use an existing image URL:
+ <div>
+ <input size="40" ng-model="newProfileInfo.avatar" ng-enter="setAvatar(newProfileInfo.avatar)" />
+ <button ng-disabled="!newProfileInfo.avatar" ng-click="setAvatar(newProfileInfo.avatar)">Update Avatar</button>
+ </div>
+ </td>
+ </tr>
+ </table>
</form>
</div>
+
<div>
<form>
- <input size="40" ng-model="newProfileInfo.avatar" ng-enter="setAvatar(newProfileInfo.avatar)" />
- <button ng-disabled="!newProfileInfo.avatar" ng-click="setAvatar(newProfileInfo.avatar)">Update Avatar</button>
+ <input size="40" ng-model="newProfileInfo.name" ng-enter="setDisplayName(newProfileInfo.name)" />
+ <button ng-disabled="!newProfileInfo.name" ng-click="setDisplayName(newProfileInfo.name)">Update Name</button>
</form>
</div>
+
<br/>
<div>
|