From 8043b3019a87b2082d641543370e6b1480336c9f Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Fri, 12 Sep 2014 17:32:54 +0400 Subject: [PATCH] Switch conversations performance improved Improved performance: replaced watchers from templates with events Conversation switch is now triggered by mouse-down, not click --- app/css/app.css | 4 +- app/js/controllers.js | 48 +++++++------ app/js/directives.js | 146 +++++++++++++++++++++++++++++++++++--- app/js/services.js | 56 +++++++++++---- app/partials/dialog.html | 2 +- app/partials/im.html | 2 +- app/partials/message.html | 12 +--- 7 files changed, 214 insertions(+), 56 deletions(-) diff --git a/app/css/app.css b/app/css/app.css index dccd99c9..7b6e0af2 100644 --- a/app/css/app.css +++ b/app/css/app.css @@ -3069,8 +3069,8 @@ a.contacts_modal_contact:hover .contacts_modal_contact_status, .is_1x .icon-delete { background-image: url(../img/icons/IconsetW_1x.png); } -.im_message_selected, -.im_message_focus { +.im_message_selected .im_message_outer_wrap, +.im_message_focus .im_message_outer_wrap { background: #f2f6fa; } diff --git a/app/js/controllers.js b/app/js/controllers.js index 703bb25f..dab19f0d 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -876,7 +876,7 @@ angular.module('myApp.controllers', []) if (found) { $scope.historyUnread = {}; - $scope.historyFocus = $scope.curDialog.messageID; + $scope.$broadcast('messages_focus', $scope.curDialog.messageID); $scope.$broadcast('ui_history_change_scroll'); } else { loadHistory(); @@ -907,7 +907,9 @@ angular.module('myApp.controllers', []) minID = historyResult.history.length >= backLimit ? historyResult.history[0] : 0; - AppMessagesManager.regroupWrappedHistory(peerHistory.messages, -backLimit); + if (AppMessagesManager.regroupWrappedHistory(peerHistory.messages, -backLimit)) { + $scope.$broadcast('messages_regroup'); + } delete $scope.state.empty; $scope.$broadcast('ui_history_append'); } else { @@ -941,9 +943,11 @@ angular.module('myApp.controllers', []) historyResult.history.length && peerHistory.messages.length < historyResult.count; if (historyResult.history.length) { - maxID = historyResult.history[historyResult.history.length - 1]; - AppMessagesManager.regroupWrappedHistory(peerHistory.messages, historyResult.history.length + 1); delete $scope.state.empty; + maxID = historyResult.history[historyResult.history.length - 1]; + if (AppMessagesManager.regroupWrappedHistory(peerHistory.messages, historyResult.history.length + 1)) { + $scope.$broadcast('messages_regroup'); + } $scope.$broadcast('ui_history_prepend'); } }); @@ -972,15 +976,12 @@ angular.module('myApp.controllers', []) else if (Config.Navigator.mobile) { limit = 20; } - else if (peerHistory.messages.length > 0) { - limit = Math.min(20, peerHistory.messages.length); - } var curJump = ++jump, inputMediaFilter = $scope.historyFilter.mediaType && {_: inputMediaFilters[$scope.historyFilter.mediaType]}, getMessagesPromise = inputMediaFilter ? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID) - : AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID, limit, backLimit); + : AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID, limit, backLimit, peerHistory.messages.length); $scope.state.mayBeHasMore = true; @@ -1011,16 +1012,19 @@ angular.module('myApp.controllers', []) }); peerHistory.messages.reverse(); - AppMessagesManager.regroupWrappedHistory(peerHistory.messages); + if (AppMessagesManager.regroupWrappedHistory(peerHistory.messages)) { + $scope.$broadcast('messages_regroup'); + } if (historyResult.unreadOffset) { $scope.historyUnreadAfter = historyResult.history[historyResult.unreadOffset - 1]; - } else { + $scope.$broadcast('messages_unread_after'); + } + else if ($scope.historyUnreadAfter) { delete $scope.historyUnreadAfter; + $scope.$broadcast('messages_unread_after'); } - - $scope.historyFocus = $scope.curDialog.messageID || 0; - + $scope.$broadcast('messages_focus', $scope.curDialog.messageID || 0); $scope.$broadcast('ui_history_change'); AppMessagesManager.readHistory($scope.curDialog.inputPeer); @@ -1091,6 +1095,7 @@ angular.module('myApp.controllers', []) $scope.$broadcast('ui_panel_update'); } } + $scope.$broadcast('messages_select'); } function selectedCancel (noBroadcast) { @@ -1101,6 +1106,7 @@ angular.module('myApp.controllers', []) if (!noBroadcast) { $scope.$broadcast('ui_panel_update'); } + $scope.$broadcast('messages_select'); } function selectedFlush () { @@ -1193,13 +1199,16 @@ angular.module('myApp.controllers', []) // console.log('append', addedMessage); // console.trace(); history.messages.push(AppMessagesManager.wrapForHistory(addedMessage.messageID)); - AppMessagesManager.regroupWrappedHistory(history.messages, -3); + if (AppMessagesManager.regroupWrappedHistory(history.messages, -3)) { + $scope.$broadcast('messages_regroup'); + } if (curPeer) { $scope.historyState.typing.splice(0, $scope.historyState.typing.length); $scope.$broadcast('ui_history_append_new', {my: addedMessage.my}); - if (addedMessage.my) { + if (addedMessage.my && $scope.historyUnreadAfter) { delete $scope.historyUnreadAfter; + $scope.$broadcast('messages_unread_after'); } // console.log('append check', $rootScope.idle.isIDLE, addedMessage.peerID, $scope.curDialog.peerID); @@ -1225,7 +1234,9 @@ angular.module('myApp.controllers', []) } }; history.messages = newMessages; - AppMessagesManager.regroupWrappedHistory(history.messages); + if (AppMessagesManager.regroupWrappedHistory(history.messages)) { + $scope.$broadcast('messages_regroup'); + } if (historyUpdate.peerID == $scope.curDialog.peerID) { $scope.state.empty = !newMessages.length; } @@ -1523,7 +1534,6 @@ angular.module('myApp.controllers', []) }; $scope.$on('history_delete', function (e, historyUpdate) { - console.log(dT(), 'delete', historyUpdate); if (historyUpdate.peerID == peerID) { if (historyUpdate.msgs[$scope.messageID]) { if ($scope.nav.hasNext) { @@ -1758,7 +1768,6 @@ angular.module('myApp.controllers', []) }).result.then(function (foundUserID) { if ($scope.userID == foundUserID) { $scope.user = AppUsersManager.getUser($scope.userID); - console.log($scope.user); } }); }; @@ -1766,7 +1775,6 @@ angular.module('myApp.controllers', []) $scope.deleteContact = function () { AppUsersManager.deleteContacts([$scope.userID]).then(function () { $scope.user = AppUsersManager.getUser($scope.userID); - console.log($scope.user); }); }; @@ -1887,8 +1895,6 @@ angular.module('myApp.controllers', []) $scope.kickFromGroup = function (userID) { var user = AppUsersManager.getUser(userID); - console.log({_: 'inputUserForeign', user_id: userID, access_hash: user.access_hash || '0'}, user); - MtpApiManager.invokeApi('messages.deleteChatUser', { chat_id: $scope.chatID, user_id: {_: 'inputUserForeign', user_id: userID, access_hash: user.access_hash || '0'} diff --git a/app/js/directives.js b/app/js/directives.js index afa68d74..83087f0c 100644 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -26,10 +26,134 @@ angular.module('myApp.directives', ['myApp.filters']) }; }) - .directive('myMessage', function() { + .directive('myMessage', function($filter) { + + var dateFilter = $filter('myDate'), + dateSplitHtml = '
', + unreadSplitHtml = '
Unread messages
', + selectedClass = 'im_message_selected', + focusClass = 'im_message_focus', + unreadClass = 'im_message_unread', + errorClass = 'im_message_error', + pendingClass = 'im_message_pending'; + return { + link: link, templateUrl: 'partials/message.html' }; + + function link($scope, element, attrs) { + var selected = false, + grouped = false, + focused = false, + error = false, + pending = false, + needDate = false, + unreadAfter = false, + applySelected = function () { + if (selected != ($scope.selectedMsgs[$scope.historyMessage.id] || false)) { + selected = !selected; + element.toggleClass(selectedClass, selected); + } + }, + needDateSplit, + applyGrouped = function () { + if (grouped != $scope.historyMessage.grouped) { + if (grouped) { + element.removeClass(grouped); + } + grouped = $scope.historyMessage.grouped; + if (grouped) { + element.addClass(grouped); + } + } + if (needDate != ($scope.historyMessage.needDate || false)) { + needDate = !needDate; + if (needDate) { + if (needDateSplit) { + needDateSplit.show(); + } else { + needDateSplit = $(dateSplitHtml); + $(needDateSplit[0].firstChild).text(dateFilter($scope.historyMessage.date)); + if (unreadAfterSplit) { + needDateSplit.insertBefore(unreadAfterSplit) + } else { + needDateSplit.prependTo(element); + } + } + } else { + needDateSplit.hide(); + } + } + }, + unreadAfterSplit; + + applySelected(); + applyGrouped(); + + $scope.$on('messages_select', applySelected); + $scope.$on('messages_regroup', applyGrouped); + + $scope.$on('messages_focus', function (e, focusedMsgID) { + if ((focusedMsgID == $scope.historyMessage.id) != focused) { + focused = !focused; + element.toggleClass(focusClass, focused); + } + }); + + if ($scope.historyMessage.unread) { + var deregisterUnreadAfter; + if (!$scope.historyMessage.out) { + var applyUnreadAfter = function () { + if (unreadAfter != ($scope.historyUnreadAfter == $scope.historyMessage.id)) { + unreadAfter = !unreadAfter; + if (unreadAfter) { + if (unreadAfterSplit) { + unreadAfterSplit.show(); + } else { + unreadAfterSplit = $(unreadSplitHtml).prependTo(element); + } + } else { + unreadAfterSplit.hide(); + if (deregisterUnreadAfter) { + deregisterUnreadAfter(); + } + } + } + }; + applyUnreadAfter(); + deregisterUnreadAfter = $scope.$on('messages_unread_after', applyUnreadAfter); + } + element.addClass(unreadClass); + var deregisterUnread = $scope.$on('messages_read', function () { + if (!$scope.historyMessage.unread) { + element.removeClass(unreadClass); + deregisterUnread(); + if (deregisterUnreadAfter && !unreadAfter) { + deregisterUnreadAfter(); + } + } + }); + } + if ($scope.historyMessage.error || $scope.historyMessage.pending) { + var applyPending = function () { + if (pending != ($scope.historyMessage.pending || false)) { + pending = !pending; + element.toggleClass(pendingClass, pending); + } + if (error != ($scope.historyMessage.error || false)) { + error = !error; + element.toggleClass(errorClass, error); + } + if (!error && !pending) { + deregisterPending(); + } + }, + deregisterPending = $scope.$on('messages_pending', applyPending); + + applyPending(); + } + } }) .directive('myServiceMessage', function() { @@ -132,9 +256,9 @@ angular.module('myApp.directives', ['myApp.filters']) } if (e.keyCode == 36 && !e.shiftKey && !e.ctrlKey && e.altKey) { // Alt + Home - var currentSelected = $(scrollableWrap).find('.im_dialog_wrap a')[0]; - if (currentSelected) { - currentSelected.click(); + var currentSelected = $(scrollableWrap).find('.im_dialog_wrap a'); + if (currentSelected.length) { + currentSelected.trigger('mousedown'); scrollableWrap.scrollTop = 0; $(dialogsWrap).nanoScroller({flash: true}); } @@ -154,7 +278,7 @@ angular.module('myApp.directives', ['myApp.filters']) if (searchFocused && e.keyCode == 13) { // Enter var currentSelected = $(scrollableWrap).find('.im_dialog_selected')[0] || $(scrollableWrap).find('.im_dialog_wrap a')[0]; if (currentSelected) { - currentSelected.click(); + $(currentSelected).trigger('mousedown'); } return cancelEvent(e); } @@ -191,7 +315,7 @@ angular.module('myApp.directives', ['myApp.filters']) if (skip) { if (nextDialogWrap) { - $(nextDialogWrap).find('a')[0].click(); + $(nextDialogWrap).find('a').trigger('mousedown'); } } else { if (currentSelectedWrap && nextDialogWrap) { @@ -505,13 +629,17 @@ angular.module('myApp.directives', ['myApp.filters']) function changeScroll () { var unreadSplit, focusMessage; + // console.log('change scroll'); if (focusMessage = $('.im_message_focus:visible', scrollableWrap)[0]) { + // console.log('change scroll focus', focusMessage.offsetTop); scrollableWrap.scrollTop = Math.max(0, focusMessage.offsetTop - Math.floor(scrollableWrap.clientHeight / 2) + 26); atBottom = false; } else if (unreadSplit = $('.im_message_unread_split:visible', scrollableWrap)[0]) { + // console.log('change scroll unread', unreadSplit.offsetTop); scrollableWrap.scrollTop = Math.max(0, unreadSplit.offsetTop - 52); atBottom = false; } else { + // console.log('change scroll bottom'); scrollableWrap.scrollTop = scrollableWrap.scrollHeight; atBottom = true; } @@ -522,11 +650,13 @@ angular.module('myApp.directives', ['myApp.filters']) }; $scope.$on('ui_history_change', function () { + var pr = parseInt($(scrollableWrap).css('paddingRight')) $(scrollableWrap).addClass('im_history_to_bottom'); - $(scrollable).css({bottom: 0}); + scrollableWrap.scrollHeight; // Some strange Chrome bug workaround + $(scrollable).css({bottom: 0, marginLeft: -Math.ceil(pr / 2)}); onContentLoaded(function () { $(scrollableWrap).removeClass('im_history_to_bottom'); - $(scrollable).css({bottom: ''}); + $(scrollable).css({bottom: '', marginLeft: ''}); updateSizes(true); moreNotified = false; lessNotified = false; diff --git a/app/js/services.js b/app/js/services.js index 6d984f18..fb95c3f6 100644 --- a/app/js/services.js +++ b/app/js/services.js @@ -901,7 +901,7 @@ angular.module('myApp.services', []) }); }; - function getHistory (inputPeer, maxID, limit, backLimit) { + function getHistory (inputPeer, maxID, limit, backLimit, prerendered) { var peerID = AppPeersManager.getPeerID(inputPeer), historyStorage = historiesStorage[peerID], offset = 0, @@ -910,6 +910,8 @@ angular.module('myApp.services', []) unreadSkip = false, resultPending = []; + prerendered = prerendered ? Math.min(50, prerendered) : 0; + if (historyStorage === undefined) { historyStorage = historiesStorage[peerID] = {count: null, history: [], pending: []}; } @@ -926,7 +928,7 @@ angular.module('myApp.services', []) unreadOffset = 6; offset = unreadCount - unreadOffset; } else { - limit = Math.max(10, unreadCount + 2); + limit = Math.max(10, prerendered, unreadCount + 2); unreadOffset = unreadCount; } } @@ -950,9 +952,8 @@ angular.module('myApp.services', []) offset = Math.max(0, offset - backLimit); limit += backLimit; } else { - limit = limit || (offset ? 20 : 5); + limit = limit || (offset ? 20 : (prerendered || 5)); } - return $q.when({ count: historyStorage.count, history: resultPending.concat(historyStorage.history.slice(offset, offset + limit)), @@ -962,12 +963,11 @@ angular.module('myApp.services', []) } if (!backLimit && !limit) { - limit = 20; + limit = prerendered || 20; } if (offsetNotFound) { offset = 0; } - if (backLimit || unreadSkip || maxID && historyStorage.history.indexOf(maxID) == -1) { if (backLimit) { offset = -backLimit; @@ -1203,6 +1203,7 @@ angular.module('myApp.services', []) // console.log('done read history', peerID); foundDialog[0].unread_count = 0; $rootScope.$broadcast('dialog_unread', {peerID: peerID, count: 0}); + $rootScope.$broadcast('messages_read'); } })['finally'](function () { delete historyStorage.readPromise; @@ -1312,6 +1313,7 @@ angular.module('myApp.services', []) delete historyMessage.error; } } + $rootScope.$broadcast('messages_pending'); } message.send = function () { @@ -1436,6 +1438,7 @@ angular.module('myApp.services', []) delete historyMessage.error; } } + $rootScope.$broadcast('messages_pending'); } message.send = function () { @@ -1577,6 +1580,7 @@ angular.module('myApp.services', []) delete historyMessage.error; } } + $rootScope.$broadcast('messages_pending'); } message.send = function () { @@ -1715,6 +1719,8 @@ angular.module('myApp.services', []) delete historyMessage.error; delete historyMessage.random_id; delete historyMessage.send; + + $rootScope.$broadcast('messages_pending'); } delete messagesForHistory[tempID]; @@ -1832,12 +1838,13 @@ angular.module('myApp.services', []) function regroupWrappedHistory (history, limit) { if (!history || !history.length) { - return; + return false; } var start = 0, len = history.length, end = len, - i, curDay, prevDay, curMessage, prevMessage; + i, curDay, prevDay, curMessage, prevMessage, curGrouped, prevGrouped, + wasUpdated = false; if (limit > 0) { end = Math.min(limit, len); @@ -1849,10 +1856,19 @@ angular.module('myApp.services', []) curMessage = history[i]; curDay = Math.floor((curMessage.date + midnightOffset) / 86400); + prevGrouped = prevMessage && prevMessage.grouped; + curGrouped = curMessage.grouped; + if (curDay === prevDay) { - delete curMessage.needDate; + if (curMessage.needDate) { + delete curMessage.needDate; + wasUpdated = true; + } } else if (!i || prevMessage) { - curMessage.needDate = true; + if (!curMessage.needDate) { + curMessage.needDate = true; + wasUpdated = true; + } } if (prevMessage && @@ -1883,9 +1899,17 @@ angular.module('myApp.services', []) prevMessage.grouped += ' im_grouped_fwd_end'; } } + if (!wasUpdated && prevGrouped != (prevMessage && prevMessage.grouped)) { + wasUpdated = true; + } prevMessage = curMessage; prevDay = curDay; } + if (!wasUpdated && curGrouped != (prevMessage && prevMessage.grouped)) { + wasUpdated = true; + } + + return wasUpdated; } function getDialogByPeerID (peerID) { @@ -2085,7 +2109,8 @@ angular.module('myApp.services', []) case 'updateReadMessages': var dialogsUpdated = {}, - messageID, message, i, peerID, foundDialog, dialog; + messageID, message, i, peerID, foundDialog, dialog, + foundAffected = false; for (i = 0; i < update.messages.length; i++) { messageID = update.messages[i]; message = messagesStorage[messageID]; @@ -2094,6 +2119,9 @@ angular.module('myApp.services', []) message.unread = false; if (messagesForHistory[messageID]) { messagesForHistory[messageID].unread = false; + if (!foundAffected) { + foundAffected = true; + } } if (messagesForDialogs[messageID]) { messagesForDialogs[messageID].unread = false; @@ -2116,6 +2144,9 @@ angular.module('myApp.services', []) angular.forEach(dialogsUpdated, function(count, peerID) { $rootScope.$broadcast('dialog_unread', {peerID: peerID, count: count}); }); + if (foundAffected) { + $rootScope.$broadcast('messages_read'); + } break; case 'updateDeleteMessages': @@ -2533,7 +2564,6 @@ angular.module('myApp.services', []) full.width = fullWidth; } } - // console.log(222, video.w, video.h, full.width, full.height); video.full = full; video.fullThumb = angular.copy(video.thumb); @@ -3565,7 +3595,7 @@ angular.module('myApp.services', []) } function notify (data) { - console.log('notify', $rootScope.idle.isIDLE, notificationsUiSupport); + // console.log('notify', $rootScope.idle.isIDLE, notificationsUiSupport); // FFOS Notification blob src bug workaround if (Config.Navigator.ffos) { diff --git a/app/partials/dialog.html b/app/partials/dialog.html index 208e1a2c..ff3b8b95 100644 --- a/app/partials/dialog.html +++ b/app/partials/dialog.html @@ -1,4 +1,4 @@ - +
diff --git a/app/partials/im.html b/app/partials/im.html index c73fb3ff..6e1f5330 100644 --- a/app/partials/im.html +++ b/app/partials/im.html @@ -56,7 +56,7 @@
Contacts