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']) @@ -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) {
return false;
}
var pos = false;
angular.forEach($scope.dialogs, function(curDialog, curPos) {
if (curDialog.peerID == dialog.peerID) {
pos = curPos;
}
var topMessages = [];
var topToDialogs = {};
angular.forEach(dialogsUpdated, function (dialog, peerID) {
topToDialogs[dialog.top_message] = dialog;
topMessages.push(dialog.top_message);
});
topMessages.sort();
var wrappedDialog = AppMessagesManager.wrapForDialog(dialog.top_message, dialog.unread_count);
if (pos !== false) {
var prev = $scope.dialogs.splice(pos, 1);
safeReplaceObject(prev, wrappedDialog);
var i, dialog;
var len = $scope.dialogs.length;
for (i = 0; i < len; i++) {
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;
if (!peersInDialogs[dialog.peerID]) {
@ -1055,6 +1068,17 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1055,6 +1068,17 @@ angular.module('myApp.controllers', ['myApp.i18n'])
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) {
var peerData = AppPeersManager.getPeer(peerID);
// console.log('update', preload, peerData);
@ -1460,15 +1484,29 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1460,15 +1484,29 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$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 = {};
$scope.$on('history_append', function (e, addedMessage) {
var history = historiesQueueFind(addedMessage.peerID);
// var history = historiesQueuePush(addedMessage.peerID);
if (!history) {
return;
}
var curPeer = addedMessage.peerID == $scope.curDialog.peerID;
if (curPeer) {
if ($scope.historyFilter.mediaType || $scope.historyState.skipped) {
if ($scope.historyFilter.mediaType ||
$scope.historyState.skipped) {
if (addedMessage.my) {
returnToRecent();
} else {
@ -1518,6 +1556,101 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -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) {
var history = historiesQueueFind(historyUpdate.peerID);
if (!history) {

68
app/js/lib/tl_utils.js

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

80
app/js/services.js

@ -792,7 +792,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -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 messagesForHistory = {};
@ -895,6 +895,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -895,6 +895,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
AppUsersManager.saveApiUsers(dialogsResult.users);
AppChatsManager.saveApiChats(dialogsResult.chats);
// return {
// count: 0,
// dialogs: []
// };
saveMessages(dialogsResult.messages);
if (maxID > 0) {
@ -2270,6 +2276,25 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -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) {
// if (update._ != 'updateUserStatus') {
// console.log('on apiUpdate', update);
@ -2320,34 +2345,45 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2320,34 +2345,45 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}
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),
dialog;
var foundDialog = getDialogByPeerID(peerID);
var dialog;
var inboxUnread = !message.out && message.unread;
if (foundDialog.length) {
dialog = foundDialog[0];
if (foundDialog[1] > 0) {
dialogsStorage.dialogs.splice(foundDialog[1], 1);
} else {
dialog = {peerID: peerID, unread_count: 0, top_message: false}
dialogsStorage.dialogs.unshift(dialog);
}
if (!message.out && message.unread) {
// console.log('inc unread count', dialog.unread_count);
dialog.top_message = message.id;
if (inboxUnread) {
dialog.unread_count++;
}
dialog.top_message = message.id;
// console.log('new message', message, peerID, historyStorage, foundDialog, dialog);
} else {
SearchIndexManager.indexObject(peerID, AppPeersManager.getPeerSearchText(peerID), dialogsIndex);
dialog = {
peerID: peerID,
unread_count: inboxUnread ? 1 : 0,
top_message: message.id
};
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) &&
!message.out &&
message.unread) {
if (inboxUnread && ($rootScope.selectedPeerID != peerID || $rootScope.idle.isIDLE)) {
var notifyPeer = message.flags & 16 ? message.from_id : peerID;
var isMutedPromise = NotificationsManager.getPeerMuted(notifyPeer);
@ -3716,12 +3752,13 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3716,12 +3752,13 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
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') {
console.log(dT(), 'apply empty diff', differenceResult.seq);
curState.date = differenceResult.date;
curState.seq = differenceResult.seq;
syncLoading = false;
$rootScope.$broadcast('stateSynchronized');
return false;
}
@ -3729,10 +3766,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3729,10 +3766,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
AppChatsManager.saveApiChats(differenceResult.chats);
// Should be first because of updateMessageID
// console.log(dT(), 'applying', differenceResult.other_updates.length, 'other updates');
angular.forEach(differenceResult.other_updates, function(update){
saveUpdate(update);
});
// console.log(dT(), 'applying', differenceResult.new_messages.length, 'new messages');
angular.forEach(differenceResult.new_messages, function (apiMessage) {
saveUpdate({
_: 'updateNewMessage',
@ -3752,6 +3791,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3752,6 +3791,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
if (differenceResult._ == 'updates.differenceSlice') {
getDifference();
} else {
// console.log(dT(), 'finished get diff');
$rootScope.$broadcast('stateSynchronized');
syncLoading = false;
}
});
@ -3855,6 +3896,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3855,6 +3896,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
setTimeout(function () {
syncLoading = false;
}, 1000);
// curState.seq = 1;
// curState.pts = stateResult.pts - 5000;
// curState.date = 1;
// getDifference();
})
}

Loading…
Cancel
Save