Browse Source

Improved large getDiff perfomance

Now grouping most UI updates

Closes #705
master
Igor Zhukov 10 years ago
parent
commit
3f13351b1c
  1. 157
      app/js/controllers.js
  2. 68
      app/js/lib/tl_utils.js
  3. 80
      app/js/services.js

157
app/js/controllers.js

@ -631,24 +631,37 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}); });
}); });
$scope.$on('dialogs_update', function (e, dialog) { $scope.$on('dialogs_multiupdate', function (e, dialogsUpdated) {
if ($scope.search.query !== undefined && $scope.search.query.length) { if ($scope.search.query !== undefined && $scope.search.query.length) {
return false; return false;
} }
var pos = false; var topMessages = [];
angular.forEach($scope.dialogs, function(curDialog, curPos) { var topToDialogs = {};
if (curDialog.peerID == dialog.peerID) { angular.forEach(dialogsUpdated, function (dialog, peerID) {
pos = curPos; topToDialogs[dialog.top_message] = dialog;
} topMessages.push(dialog.top_message);
}); });
topMessages.sort();
var wrappedDialog = AppMessagesManager.wrapForDialog(dialog.top_message, dialog.unread_count); var i, dialog;
if (pos !== false) { var len = $scope.dialogs.length;
var prev = $scope.dialogs.splice(pos, 1); for (i = 0; i < len; i++) {
safeReplaceObject(prev, wrappedDialog); dialog = $scope.dialogs[i];
if (dialogsUpdated[dialog.peerID]) {
$scope.dialogs.splice(i, 1);
i--;
len--;
}
} }
$scope.dialogs.unshift(wrappedDialog); len = topMessages.length;
for (i = 0; i < len; i++) {
dialog = topToDialogs[topMessages[i]];
$scope.dialogs.unshift(
AppMessagesManager.wrapForDialog(dialog.top_message, dialog.unread_count)
);
}
delete $scope.isEmpty.dialogs; delete $scope.isEmpty.dialogs;
if (!peersInDialogs[dialog.peerID]) { if (!peersInDialogs[dialog.peerID]) {
@ -1055,6 +1068,17 @@ angular.module('myApp.controllers', ['myApp.i18n'])
return false; return false;
} }
function historiesQueuePop (peerID) {
var i;
for (i = 0; i < $scope.peerHistories.length; i++) {
if ($scope.peerHistories[i].peerID == peerID) {
$scope.peerHistories.splice(i, 1);
return true;
}
}
return false;
}
function updateHistoryPeer(preload) { function updateHistoryPeer(preload) {
var peerData = AppPeersManager.getPeer(peerID); var peerData = AppPeersManager.getPeer(peerID);
// console.log('update', preload, peerData); // console.log('update', preload, peerData);
@ -1460,15 +1484,29 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$on('history_update', angular.noop); $scope.$on('history_update', angular.noop);
var loadAfterSync = false;
$scope.$on('stateSynchronized', function () {
if (!loadAfterSync) {
return;
}
if (loadAfterSync == $scope.curDialog.peerID) {
loadHistory();
}
loadAfterSync = false;
});
var typingTimeouts = {}; var typingTimeouts = {};
$scope.$on('history_append', function (e, addedMessage) { $scope.$on('history_append', function (e, addedMessage) {
var history = historiesQueueFind(addedMessage.peerID); var history = historiesQueueFind(addedMessage.peerID);
// var history = historiesQueuePush(addedMessage.peerID);
if (!history) { if (!history) {
return; return;
} }
var curPeer = addedMessage.peerID == $scope.curDialog.peerID; var curPeer = addedMessage.peerID == $scope.curDialog.peerID;
if (curPeer) { if (curPeer) {
if ($scope.historyFilter.mediaType || $scope.historyState.skipped) { if ($scope.historyFilter.mediaType ||
$scope.historyState.skipped) {
if (addedMessage.my) { if (addedMessage.my) {
returnToRecent(); returnToRecent();
} else { } else {
@ -1518,6 +1556,101 @@ angular.module('myApp.controllers', ['myApp.i18n'])
} }
}); });
$scope.$on('history_multiappend', function (e, historyMultiAdded) {
// console.log(dT(), 'multiappend', historyMultiAdded);
var regroupped = false;
var unreadAfterChanged = false;
var isIDLE = $rootScope.idle.isIDLE;
angular.forEach(historyMultiAdded, function (msgs, peerID) {
var history = historiesQueueFind(peerID);
// var history = historiesQueuePush(peerID);
if (!history) {
return;
}
var curPeer = peerID == $scope.curDialog.peerID;
var len = msgs.length;
if (curPeer) {
if ($scope.historyFilter.mediaType ||
$scope.historyState.skipped) {
$scope.historyState.missedCount += len;
return;
}
delete $scope.state.empty;
}
if (len > 10) {
if (curPeer) {
var exlen = history.messages.length;
if (exlen > 10) {
minID = history.messages[exlen - 1].id;
$scope.historyState.skipped = hasLess = minID > 0;
if (hasLess) {
loadAfterSync = peerID;
$scope.$broadcast('ui_history_append');
return;
}
}
} else {
historiesQueuePop(peerID);
return;
}
}
var messageID, historyMessage, i;
var hasOut = false;
var unreadAfterNew = false;
var lastIsRead = !(history.messages[history.messages.length - 1] || {}).unread;
for (i = 0; i < len; i++) {
messageID = msgs[i];
historyMessage = AppMessagesManager.wrapForHistory(messageID);
history.messages.push(historyMessage);
if (!unreadAfterNew && isIDLE) {
if (historyMessage.unread &&
!historyMessage.out &&
lastIsRead) {
unreadAfterNew = messageID;
} else {
lastIsRead = !historyMessage.unread;
}
}
if (!hasOut && historyMessage.out) {
hasOut = true;
}
}
if (AppMessagesManager.regroupWrappedHistory(history.messages, -len - 2)) {
regroupped = true;
}
if (curPeer) {
if ($scope.historyState.typing.length) {
$scope.historyState.typing.splice(0, $scope.historyState.typing.length);
}
$scope.$broadcast('ui_history_append_new', {
idleScroll: unreadAfterIdle && !hasOut && unreadAfterNew
});
if (isIDLE) {
if (unreadAfterNew) {
$scope.historyUnreadAfter = unreadAfterNew;
unreadAfterIdle = true;
unreadAfterChanged = true;
}
} else {
$timeout(function () {
AppMessagesManager.readHistory($scope.curDialog.inputPeer);
});
}
}
});
if (regroupped) {
$scope.$broadcast('messages_regroup');
}
if (unreadAfterChanged) {
$scope.$broadcast('messages_unread_after');
}
});
$scope.$on('history_delete', function (e, historyUpdate) { $scope.$on('history_delete', function (e, historyUpdate) {
var history = historiesQueueFind(historyUpdate.peerID); var history = historiesQueueFind(historyUpdate.peerID);
if (!history) { if (!history) {

68
app/js/lib/tl_utils.js

@ -226,20 +226,22 @@ TLSerialization.prototype.storeMethod = function (methodName, params) {
this.storeInt(intToUint(methodData.id), methodName + '[id]'); this.storeInt(intToUint(methodData.id), methodName + '[id]');
var self = this; var param, type, i, condType, fieldBit;
angular.forEach(methodData.params, function (param) { var len = methodData.params.length;
var type = param.type; for (i = 0; i < len; i++) {
param = methodData.params[i];
type = param.type;
if (type.indexOf('?') !== -1) { if (type.indexOf('?') !== -1) {
var condType = type.split('?'); condType = type.split('?');
var fieldBit = condType[0].split('.'); fieldBit = condType[0].split('.');
if (!(params[fieldBit[0]] & (1 << fieldBit[1]))) { if (!(params[fieldBit[0]] & (1 << fieldBit[1]))) {
return; continue;
} }
type = condType[1]; type = condType[1];
} }
self.storeObject(params[param.name], type, methodName + '[' + param.name + ']'); this.storeObject(params[param.name], type, methodName + '[' + param.name + ']');
}); }
return methodData.type; return methodData.type;
}; };
@ -308,20 +310,22 @@ TLSerialization.prototype.storeObject = function (obj, type, field) {
this.writeInt(intToUint(constructorData.id), field + '[' + predicate + '][id]'); this.writeInt(intToUint(constructorData.id), field + '[' + predicate + '][id]');
} }
var self = this; var param, type, i, condType, fieldBit;
angular.forEach(constructorData.params, function (param) { var len = constructorData.params.length;
var type = param.type; for (i = 0; i < len; i++) {
param = constructorData.params[i];
type = param.type;
if (type.indexOf('?') !== -1) { if (type.indexOf('?') !== -1) {
var condType = type.split('?'); condType = type.split('?');
var fieldBit = condType[0].split('.'); fieldBit = condType[0].split('.');
if (!(obj[fieldBit[0]] & (1 << fieldBit[1]))) { if (!(obj[fieldBit[0]] & (1 << fieldBit[1]))) {
return; continue;
} }
type = condType[1]; type = condType[1];
} }
self.storeObject(obj[param.name], type, field + '[' + predicate + '][' + param.name + ']'); this.storeObject(obj[param.name], type, field + '[' + predicate + '][' + param.name + ']');
}); }
return constructorData.type; return constructorData.type;
}; };
@ -532,7 +536,7 @@ TLDeserialization.prototype.fetchObject = function (type, field) {
if (type.charAt(0) == '%') { if (type.charAt(0) == '%') {
var checkType = type.substr(1); var checkType = type.substr(1);
for (i = 0; i < schema.constructors.length; i++) { for (var i = 0; i < schema.constructors.length; i++) {
if (schema.constructors[i].type == checkType) { if (schema.constructors[i].type == checkType) {
constructorData = schema.constructors[i]; constructorData = schema.constructors[i];
break break
@ -543,7 +547,7 @@ TLDeserialization.prototype.fetchObject = function (type, field) {
} }
} }
else if (type.charAt(0) >= 97 && type.charAt(0) <= 122) { else if (type.charAt(0) >= 97 && type.charAt(0) <= 122) {
for (i = 0; i < schema.constructors.length; i++) { for (var i = 0; i < schema.constructors.length; i++) {
if (schema.constructors[i].predicate == type) { if (schema.constructors[i].predicate == type) {
constructorData = schema.constructors[i]; constructorData = schema.constructors[i];
break break
@ -566,12 +570,17 @@ TLDeserialization.prototype.fetchObject = function (type, field) {
return newDeserializer.fetchObject(type, field); return newDeserializer.fetchObject(type, field);
} }
for (i = 0; i < schema.constructors.length; i++) { var index = schema.constructorsIndex;
if (schema.constructors[i].id == constructorCmp) { if (!index) {
constructorData = schema.constructors[i]; schema.constructorsIndex = index = {};
break; for (var i = 0; i < schema.constructors.length; i++) {
index[schema.constructors[i].id] = i;
} }
} }
var i = index[constructorCmp];
if (i) {
constructorData = schema.constructors[i];
}
var fallback = false; var fallback = false;
if (!constructorData && this.mtproto) { if (!constructorData && this.mtproto) {
@ -601,19 +610,22 @@ TLDeserialization.prototype.fetchObject = function (type, field) {
if (this.override[overrideKey]) { if (this.override[overrideKey]) {
this.override[overrideKey].apply(this, [result, field + '[' + predicate + ']']); this.override[overrideKey].apply(this, [result, field + '[' + predicate + ']']);
} else { } else {
angular.forEach(constructorData.params, function (param) { var i, param, type, condType, fieldBit;
var type = param.type; var len = constructorData.params.length;
for (i = 0; i < len; i++) {
param = constructorData.params[i];
type = param.type;
if (type.indexOf('?') !== -1) { if (type.indexOf('?') !== -1) {
var condType = type.split('?'); condType = type.split('?');
var fieldBit = condType[0].split('.'); fieldBit = condType[0].split('.');
if (!(result[fieldBit[0]] & (1 << fieldBit[1]))) { if (!(result[fieldBit[0]] & (1 << fieldBit[1]))) {
return; continue;
} }
type = condType[1]; type = condType[1];
} }
result[param.name] = self.fetchObject(type, field + '[' + predicate + '][' + param.name + ']'); result[param.name] = self.fetchObject(type, field + '[' + predicate + '][' + param.name + ']');
}); }
} }
if (fallback) { if (fallback) {

80
app/js/services.js

@ -792,7 +792,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
} }
}) })
.service('AppMessagesManager', function ($q, $rootScope, $location, $filter, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, AppAudioManager, AppWebPagesManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, PeersSelectService, Storage, FileManager, TelegramMeWebService, StatusManager, _) { .service('AppMessagesManager', function ($q, $rootScope, $location, $filter, $timeout, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, AppAudioManager, AppWebPagesManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, PeersSelectService, Storage, FileManager, TelegramMeWebService, StatusManager, _) {
var messagesStorage = {}; var messagesStorage = {};
var messagesForHistory = {}; var messagesForHistory = {};
@ -895,6 +895,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
AppUsersManager.saveApiUsers(dialogsResult.users); AppUsersManager.saveApiUsers(dialogsResult.users);
AppChatsManager.saveApiChats(dialogsResult.chats); AppChatsManager.saveApiChats(dialogsResult.chats);
// return {
// count: 0,
// dialogs: []
// };
saveMessages(dialogsResult.messages); saveMessages(dialogsResult.messages);
if (maxID > 0) { if (maxID > 0) {
@ -2270,6 +2276,25 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}); });
} }
var newMessagesHandlePromise = false;
var newMessagesToHandle = {};
var newDialogsHandlePromise = false;
var newDialogsToHandle = {};
function handleNewMessages () {
$timeout.cancel(newMessagesHandlePromise);
newMessagesHandlePromise = false;
$rootScope.$broadcast('history_multiappend', newMessagesToHandle);
newMessagesToHandle = {};
}
function handleNewDialogs () {
$timeout.cancel(newDialogsHandlePromise);
newDialogsHandlePromise = false;
$rootScope.$broadcast('dialogs_multiupdate', newDialogsToHandle);
newDialogsToHandle = {};
}
$rootScope.$on('apiUpdate', function (e, update) { $rootScope.$on('apiUpdate', function (e, update) {
// if (update._ != 'updateUserStatus') { // if (update._ != 'updateUserStatus') {
// console.log('on apiUpdate', update); // console.log('on apiUpdate', update);
@ -2320,34 +2345,45 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
} }
if (!pendingMessage) { if (!pendingMessage) {
$rootScope.$broadcast('history_append', {peerID: peerID, messageID: message.id}); if (newMessagesToHandle[peerID] === undefined) {
newMessagesToHandle[peerID] = [];
}
newMessagesToHandle[peerID].push(message.id);
if (!newMessagesHandlePromise) {
newMessagesHandlePromise = $timeout(handleNewMessages, 0);
}
} }
var foundDialog = getDialogByPeerID(peerID), var foundDialog = getDialogByPeerID(peerID);
dialog; var dialog;
var inboxUnread = !message.out && message.unread;
if (foundDialog.length) { if (foundDialog.length) {
dialog = foundDialog[0]; dialog = foundDialog[0];
if (foundDialog[1] > 0) {
dialogsStorage.dialogs.splice(foundDialog[1], 1); dialogsStorage.dialogs.splice(foundDialog[1], 1);
} else { dialogsStorage.dialogs.unshift(dialog);
dialog = {peerID: peerID, unread_count: 0, top_message: false}
} }
if (!message.out && message.unread) { dialog.top_message = message.id;
// console.log('inc unread count', dialog.unread_count); if (inboxUnread) {
dialog.unread_count++; dialog.unread_count++;
} }
dialog.top_message = message.id; } else {
// console.log('new message', message, peerID, historyStorage, foundDialog, dialog);
SearchIndexManager.indexObject(peerID, AppPeersManager.getPeerSearchText(peerID), dialogsIndex); SearchIndexManager.indexObject(peerID, AppPeersManager.getPeerSearchText(peerID), dialogsIndex);
dialog = {
peerID: peerID,
unread_count: inboxUnread ? 1 : 0,
top_message: message.id
};
dialogsStorage.dialogs.unshift(dialog); dialogsStorage.dialogs.unshift(dialog);
$rootScope.$broadcast('dialogs_update', dialog); }
newDialogsToHandle[peerID] = dialog;
if (!newDialogsHandlePromise) {
newDialogsHandlePromise = $timeout(handleNewDialogs, 0);
}
if (($rootScope.selectedPeerID != peerID || $rootScope.idle.isIDLE) && if (inboxUnread && ($rootScope.selectedPeerID != peerID || $rootScope.idle.isIDLE)) {
!message.out &&
message.unread) {
var notifyPeer = message.flags & 16 ? message.from_id : peerID; var notifyPeer = message.flags & 16 ? message.from_id : peerID;
var isMutedPromise = NotificationsManager.getPeerMuted(notifyPeer); var isMutedPromise = NotificationsManager.getPeerMuted(notifyPeer);
@ -3716,12 +3752,13 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
syncPending = false; syncPending = false;
} }
MtpApiManager.invokeApi('updates.getDifference', {pts: curState.pts, date: curState.date, qts: 0}).then(function (differenceResult) { MtpApiManager.invokeApi('updates.getDifference', {pts: curState.pts, date: curState.date, qts: -1}).then(function (differenceResult) {
if (differenceResult._ == 'updates.differenceEmpty') { if (differenceResult._ == 'updates.differenceEmpty') {
console.log(dT(), 'apply empty diff', differenceResult.seq); console.log(dT(), 'apply empty diff', differenceResult.seq);
curState.date = differenceResult.date; curState.date = differenceResult.date;
curState.seq = differenceResult.seq; curState.seq = differenceResult.seq;
syncLoading = false; syncLoading = false;
$rootScope.$broadcast('stateSynchronized');
return false; return false;
} }
@ -3729,10 +3766,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
AppChatsManager.saveApiChats(differenceResult.chats); AppChatsManager.saveApiChats(differenceResult.chats);
// Should be first because of updateMessageID // Should be first because of updateMessageID
// console.log(dT(), 'applying', differenceResult.other_updates.length, 'other updates');
angular.forEach(differenceResult.other_updates, function(update){ angular.forEach(differenceResult.other_updates, function(update){
saveUpdate(update); saveUpdate(update);
}); });
// console.log(dT(), 'applying', differenceResult.new_messages.length, 'new messages');
angular.forEach(differenceResult.new_messages, function (apiMessage) { angular.forEach(differenceResult.new_messages, function (apiMessage) {
saveUpdate({ saveUpdate({
_: 'updateNewMessage', _: 'updateNewMessage',
@ -3752,6 +3791,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
if (differenceResult._ == 'updates.differenceSlice') { if (differenceResult._ == 'updates.differenceSlice') {
getDifference(); getDifference();
} else { } else {
// console.log(dT(), 'finished get diff');
$rootScope.$broadcast('stateSynchronized');
syncLoading = false; syncLoading = false;
} }
}); });
@ -3855,6 +3896,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
setTimeout(function () { setTimeout(function () {
syncLoading = false; syncLoading = false;
}, 1000); }, 1000);
// curState.seq = 1;
// curState.pts = stateResult.pts - 5000;
// curState.date = 1;
// getDifference();
}) })
} }

Loading…
Cancel
Save