Merge branch 'master' of github.com:matrix-org/synapse into sql_refactor

paul/schema_breaking_changes
Erik Johnston 2014-08-15 17:50:27 +01:00
commit 7f5c7ddea9
11 changed files with 173 additions and 28 deletions

View File

@ -694,6 +694,16 @@ Invite/Joining/Leaving a room
Required keys: Required keys:
membership : [join|invite] - The membership state of $user_id in room membership : [join|invite] - The membership state of $user_id in room
$room_id. $room_id.
Optional keys:
displayname,
avatar_url : String fields from the member user's profile
state,
status_msg,
mtime_age : Presence information
These optional keys provide extra information that the client is likely to
be interested in so it doesn't have to perform an additional profile or
presence information fetch.
Where: Where:
join - Indicate you ($user_id) are joining the room $room_id. join - Indicate you ($user_id) are joining the room $room_id.

View File

@ -380,14 +380,23 @@ class PresenceHandler(BaseHandler):
logger.debug("Start polling for presence from %s", user) logger.debug("Start polling for presence from %s", user)
if target_user: if target_user:
target_users = [target_user] target_users = set(target_user)
else: else:
presence = yield self.store.get_presence_list( presence = yield self.store.get_presence_list(
user.localpart, accepted=True user.localpart, accepted=True
) )
target_users = [ target_users = set([
self.hs.parse_userid(x["observed_user_id"]) for x in presence self.hs.parse_userid(x["observed_user_id"]) for x in presence
] ])
# Also include people in all my rooms
rm_handler = self.homeserver.get_handlers().room_member_handler
room_ids = yield rm_handler.get_rooms_for_user(user)
for room_id in room_ids:
for member in (yield rm_handler.get_room_members(room_id)):
target_users.add(member)
if state is None: if state is None:
state = yield self.store.get_presence_state(user.localpart) state = yield self.store.get_presence_state(user.localpart)

View File

@ -22,7 +22,8 @@ var matrixWebClient = angular.module('matrixWebClient', [
'RoomsController', 'RoomsController',
'matrixService', 'matrixService',
'eventStreamService', 'eventStreamService',
'eventHandlerService' 'eventHandlerService',
'infinite-scroll'
]); ]);
matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider', matrixWebClient.config(['$routeProvider', '$provide', '$httpProvider',

View File

@ -33,23 +33,24 @@ angular.module('eventHandlerService', [])
var PRESENCE_EVENT = "PRESENCE_EVENT"; var PRESENCE_EVENT = "PRESENCE_EVENT";
$rootScope.events = { $rootScope.events = {
rooms: {}, // will contain roomId: { messages:[], members:[] } rooms: {}, // will contain roomId: { messages:[], members:{userid1: event} }
}; };
var initRoom = function(room_id) { var initRoom = function(room_id) {
console.log("Creating new handler entry for " + room_id); if (!(room_id in $rootScope.events.rooms)) {
$rootScope.events.rooms[room_id] = {}; console.log("Creating new handler entry for " + room_id);
$rootScope.events.rooms[room_id].messages = []; $rootScope.events.rooms[room_id] = {};
$rootScope.events.rooms[room_id].members = []; $rootScope.events.rooms[room_id].messages = [];
$rootScope.events.rooms[room_id].members = {};
}
} }
var handleMessage = function(event, isLiveEvent) { var handleMessage = function(event, isLiveEvent) {
if ("membership_target" in event.content) { if ("membership_target" in event.content) {
event.user_id = event.content.membership_target; event.user_id = event.content.membership_target;
} }
if (!(event.room_id in $rootScope.events.rooms)) {
initRoom(event.room_id); initRoom(event.room_id);
}
if (isLiveEvent) { if (isLiveEvent) {
$rootScope.events.rooms[event.room_id].messages.push(event); $rootScope.events.rooms[event.room_id].messages.push(event);
@ -67,6 +68,8 @@ angular.module('eventHandlerService', [])
}; };
var handleRoomMember = function(event, isLiveEvent) { var handleRoomMember = function(event, isLiveEvent) {
initRoom(event.room_id);
$rootScope.events.rooms[event.room_id].members[event.user_id] = event;
$rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent); $rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent);
}; };

View File

@ -5,9 +5,11 @@
<link rel="stylesheet" href="app.css"> <link rel="stylesheet" href="app.css">
<link rel="icon" href="favicon.ico"> <link rel="icon" href="favicon.ico">
<script type='text/javascript' src='js/jquery-1.8.3.min.js'></script>
<script src="js/angular.js"></script> <script src="js/angular.js"></script>
<script src="js/angular-route.js"></script> <script src="js/angular-route.js"></script>
<script type='text/javascript' src='js/ng-infinite-scroll-matrix.js'></script>
<script src="app.js"></script> <script src="app.js"></script>
<script src="app-controller.js"></script> <script src="app-controller.js"></script>
<script src="login/login-controller.js"></script> <script src="login/login-controller.js"></script>

2
webclient/js/jquery-1.8.3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,63 @@
/* ng-infinite-scroll - v1.0.0 - 2013-02-23
Matrix: Modified to support scrolling UP to get infinite loading and to listen
to scroll events on the PARENT element, not the window.
*/
var mod;
mod = angular.module('infinite-scroll', []);
mod.directive('infiniteScroll', [
'$rootScope', '$window', '$timeout', function($rootScope, $window, $timeout) {
return {
link: function(scope, elem, attrs) {
var checkWhenEnabled, handler, scrollDistance, scrollEnabled;
$window = angular.element($window);
scrollDistance = 0;
if (attrs.infiniteScrollDistance != null) {
scope.$watch(attrs.infiniteScrollDistance, function(value) {
return scrollDistance = parseInt(value, 10);
});
}
scrollEnabled = true;
checkWhenEnabled = false;
if (attrs.infiniteScrollDisabled != null) {
scope.$watch(attrs.infiniteScrollDisabled, function(value) {
scrollEnabled = !value;
if (scrollEnabled && checkWhenEnabled) {
checkWhenEnabled = false;
return handler();
}
});
}
handler = function() {
var elementTop, remaining, shouldScroll, windowTop;
windowTop = 0;
elementTop = elem.offset().top;
shouldScroll = elementTop >= 0; // top of list is at the top of the window or further down the page
if (shouldScroll && scrollEnabled) {
if ($rootScope.$$phase) {
return scope.$eval(attrs.infiniteScroll);
} else {
return scope.$apply(attrs.infiniteScroll);
}
} else if (shouldScroll) {
return checkWhenEnabled = true;
}
};
elem.parent().on('scroll', handler);
scope.$on('$destroy', function() {
return elem.parent().off('scroll', handler);
});
return $timeout((function() {
if (attrs.infiniteScrollImmediateCheck) {
if (scope.$eval(attrs.infiniteScrollImmediateCheck)) {
return handler();
}
} else {
return handler();
}
}), 0);
}
};
}
]);

View File

@ -15,6 +15,32 @@ limitations under the License.
*/ */
angular.module('RoomController', []) angular.module('RoomController', [])
// FIXME move directives outta here!
.directive("keepScroll", function(){
return {
controller : function($scope){
var element = 0;
this.setElement = function(el){
element = el;
}
this.addItem = function(item){
element.scrollTop = (element.scrollTop+item.clientHeight+1); //1px for margin
};
},
link : function(scope,el,attr, ctrl) {
ctrl.setElement(el[0]);
}
};
})
// FIXME move directives outta here!
.directive("scrollItem", function(){
return{
require : "^keepScroll",
link : function(scope, el, att, scrCtrl){
scrCtrl.addItem(el[0]);
}
}
})
.controller('RoomController', ['$scope', '$http', '$timeout', '$routeParams', '$location', 'matrixService', 'eventStreamService', 'eventHandlerService', .controller('RoomController', ['$scope', '$http', '$timeout', '$routeParams', '$location', 'matrixService', 'eventStreamService', 'eventHandlerService',
function($scope, $http, $timeout, $routeParams, $location, matrixService, eventStreamService, eventHandlerService) { function($scope, $http, $timeout, $routeParams, $location, matrixService, eventStreamService, eventHandlerService) {
'use strict'; 'use strict';
@ -54,7 +80,14 @@ angular.module('RoomController', [])
updatePresence(event); updatePresence(event);
}); });
var paginate = function(numItems) { $scope.paginateMore = function() {
if ($scope.state.can_paginate) {
console.log("Paginating more.");
paginate(MESSAGES_PER_PAGINATION, false);
}
};
var paginate = function(numItems, toBottom) {
matrixService.paginateBackMessages($scope.room_id, $scope.state.earliest_token, numItems).then( matrixService.paginateBackMessages($scope.room_id, $scope.state.earliest_token, numItems).then(
function(response) { function(response) {
eventHandlerService.handleEvents(response.data.chunk, false); eventHandlerService.handleEvents(response.data.chunk, false);
@ -63,6 +96,11 @@ angular.module('RoomController', [])
// no more messages to paginate :( // no more messages to paginate :(
$scope.state.can_paginate = false; $scope.state.can_paginate = false;
} }
if (toBottom) {
console.log("Scrolling to bottom");
scrollToBottom();
}
}, },
function(error) { function(error) {
console.log("Failed to paginateBackMessages: " + JSON.stringify(error)); console.log("Failed to paginateBackMessages: " + JSON.stringify(error));
@ -73,6 +111,10 @@ angular.module('RoomController', [])
var updateMemberList = function(chunk) { var updateMemberList = function(chunk) {
var isNewMember = !(chunk.target_user_id in $scope.members); var isNewMember = !(chunk.target_user_id in $scope.members);
if (isNewMember) { if (isNewMember) {
if ("state" in chunk.content) {
chunk.presenceState = chunk.content.state;
}
$scope.members[chunk.target_user_id] = chunk; $scope.members[chunk.target_user_id] = chunk;
// get their display name and profile picture and set it to their // get their display name and profile picture and set it to their
// member entry in $scope.members. We HAVE to use $timeout with 0 delay // member entry in $scope.members. We HAVE to use $timeout with 0 delay
@ -181,7 +223,7 @@ angular.module('RoomController', [])
} }
); );
paginate(MESSAGES_PER_PAGINATION); paginate(MESSAGES_PER_PAGINATION, true);
}, },
function(reason) { function(reason) {
$scope.feedback = "Can't join room: " + reason; $scope.feedback = "Can't join room: " + reason;
@ -223,6 +265,6 @@ angular.module('RoomController', [])
}; };
$scope.loadMoreHistory = function() { $scope.loadMoreHistory = function() {
paginate(MESSAGES_PER_PAGINATION); paginate(MESSAGES_PER_PAGINATION, false);
}; };
}]); }]);

View File

@ -20,9 +20,9 @@
</table> </table>
</div> </div>
<div class="messageTableWrapper"> <div class="messageTableWrapper" keep-scroll>
<table class="messageTable"> <table class="messageTable" infinite-scroll="paginateMore()">
<tr ng-repeat="msg in events.rooms[room_id].messages" ng-class="msg.user_id === state.user_id ? 'mine' : ''"> <tr ng-repeat="msg in events.rooms[room_id].messages" ng-class="msg.user_id === state.user_id ? 'mine' : ''" scroll-item>
<td class="leftBlock"> <td class="leftBlock">
<div class="sender" ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id">{{ members[msg.user_id].displayname || msg.user_id }}</div> <div class="sender" ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id">{{ members[msg.user_id].displayname || msg.user_id }}</div>
<div class="timestamp">{{ msg.content.hsob_ts | date:'MMM d HH:mm:ss' }}</div> <div class="timestamp">{{ msg.content.hsob_ts | date:'MMM d HH:mm:ss' }}</div>

View File

@ -16,11 +16,11 @@ limitations under the License.
'use strict'; 'use strict';
angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload']) angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload', 'eventHandlerService'])
.controller('RoomsController', ['$scope', '$location', 'matrixService', 'mFileUpload', .controller('RoomsController', ['$scope', '$location', 'matrixService', 'mFileUpload', 'eventHandlerService',
function($scope, $location, matrixService, mFileUpload) { function($scope, $location, matrixService, mFileUpload, eventHandlerService) {
$scope.rooms = []; $scope.rooms = {};
$scope.public_rooms = []; $scope.public_rooms = [];
$scope.newRoomId = ""; $scope.newRoomId = "";
$scope.feedback = ""; $scope.feedback = "";
@ -52,6 +52,18 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload']
linkedEmailList: matrixService.config().emailList // linked email list linkedEmailList: matrixService.config().emailList // linked email list
}; };
$scope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) {
var config = matrixService.config();
if (event.target_user_id === 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_alias = event.user_id + "'s room";
$scope.rooms[event.room_id] = event;
}
});
var assignRoomAliases = function(data) { var assignRoomAliases = function(data) {
for (var i=0; i<data.length; i++) { for (var i=0; i<data.length; i++) {
var alias = matrixService.getRoomIdToAliasMapping(data[i].room_id); var alias = matrixService.getRoomIdToAliasMapping(data[i].room_id);
@ -73,12 +85,13 @@ angular.module('RoomsController', ['matrixService', 'mFileInput', 'mFileUpload']
$scope.refresh = function() { $scope.refresh = function() {
// List all rooms joined or been invited to // List all rooms joined or been invited to
$scope.rooms = matrixService.rooms();
matrixService.rooms().then( matrixService.rooms().then(
function(response) { function(response) {
var data = assignRoomAliases(response.data); var data = assignRoomAliases(response.data);
$scope.feedback = "Success"; $scope.feedback = "Success";
$scope.rooms = data; for (var i=0; i<data.length; i++) {
$scope.rooms[data[i].room_id] = data[i];
}
}, },
function(error) { function(error) {
$scope.feedback = "Failure: " + error.data; $scope.feedback = "Failure: " + error.data;

View File

@ -61,9 +61,9 @@
<h3>My rooms</h3> <h3>My rooms</h3>
<div class="rooms" ng-repeat="room in rooms"> <div class="rooms" ng-repeat="(rm_id, room) in rooms">
<div> <div>
<a href="#/room/{{ room.room_id }}" >{{ room.room_alias }}</a> <a href="#/room/{{ rm_id }}" >{{ room.room_alias }}</a> {{room.membership === 'invite' ? ' (invited)' : ''}}
</div> </div>
</div> </div>
<br/> <br/>
@ -86,7 +86,7 @@
</div> </div>
<div> <div>
<form> <form>
<input size="40" ng-model="joinAlias.room_alias" ng-enter="joinAlias(joinAlias.room_alias)" placeholder="(e.g. #foo_channe:example.org)"/> <input size="40" ng-model="joinAlias.room_alias" ng-enter="joinAlias(joinAlias.room_alias)" placeholder="(e.g. #foo_channel:example.org)"/>
<button ng-disabled="!joinAlias.room_alias" ng-click="joinAlias(joinAlias.room_alias)">Join room</button> <button ng-disabled="!joinAlias.room_alias" ng-click="joinAlias(joinAlias.room_alias)">Join room</button>
</form> </form>
</div> </div>