Browse Source

Implemented history search

History search: added 2 tabs while searching, scroll to found message
in history
Move to 15th layer
Improve typing event (now only on value change)
Fixed styles from
https://github.com/zhukov/webogram/issues/286#issuecomment-45406436
Closes #137
master
Igor Zhukov 11 years ago
parent
commit
8556d03d9f
  1. 53
      app/css/app.css
  2. 268
      app/js/controllers.js
  3. 84
      app/js/directives.js
  4. 2
      app/js/init.js
  5. 11
      app/js/lib/config.js
  6. 2
      app/js/lib/mtproto.js
  7. 76
      app/js/services.js
  8. 4
      app/partials/dialog.html
  9. 19
      app/partials/im.html
  10. 2
      app/partials/message.html

53
app/css/app.css

@ -910,7 +910,7 @@ a.tg_radio_on:hover i.icon-radio {
} }
.im_dialogs_panel { .im_dialogs_panel {
padding: 14px 12px; padding: 12px 12px 6px;
position: relative; position: relative;
} }
.im_dialogs_search { .im_dialogs_search {
@ -927,8 +927,7 @@ a.tg_radio_on:hover i.icon-radio {
border: 1px solid #F2F2F2; border: 1px solid #F2F2F2;
border-radius: 3px; border-radius: 3px;
padding: 6px 20px 6px 30px; padding: 6px 20px 6px 30px;
margin-bottom: 0; margin: 0 0 6px;
margin: 0;
} }
.is_1x .im_dialogs_search_field { .is_1x .im_dialogs_search_field {
background-image: url(../img/icons/IconsetW_1x.png); background-image: url(../img/icons/IconsetW_1x.png);
@ -941,7 +940,7 @@ a.tg_radio_on:hover i.icon-radio {
.im_dialogs_search_clear { .im_dialogs_search_clear {
position: absolute; position: absolute;
right: 9px; right: 9px;
margin-top: -23px; margin-top: -30px;
color: #999; color: #999;
width: 13px; width: 13px;
height: 13px; height: 13px;
@ -957,6 +956,37 @@ a.tg_radio_on:hover i.icon-radio {
opacity: 1; opacity: 1;
} }
.im_dialogs_tabs {
padding: 4px 0;
position: relative;
}
.im_dialogs_tab {
color: #8c8c8c;
background: #f2f2f2;
height: 30px;
text-align: center;
overflow: hidden;
width: 50%;
float: left;
padding: 7px 0;
}
.im_dialogs_tab:hover,
.im_dialogs_tab:active {
color: #8c8c8c;
text-decoration: none;
}
.im_dialogs_tab.active {
color: #FFF;
background: #6490b1;
}
.im_dialogs_tab:first-child {
border-radius: 2px 0 0 2px;
}
.im_dialogs_tab:last-child {
border-radius: 0 2px 2px 0;
}
.im_dialogs_panel_dropdown { .im_dialogs_panel_dropdown {
position: absolute; position: absolute;
right: 12px; right: 12px;
@ -1499,7 +1529,8 @@ div.im_message_video_thumb {
overflow: hidden; overflow: hidden;
margin-top: 5px; margin-top: 5px;
} }
.im_message_iframe_video iframe { .im_message_iframe_video iframe,
.im_message_iframe_video webview {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -2937,6 +2968,15 @@ a.contacts_modal_contact:hover .contacts_modal_contact_status {
.im_message_selected { .im_message_selected {
background: #f2f6fa; background: #f2f6fa;
} }
.im_message_focus {
background-color: #f2f6fa;
-webkit-transition: background-color linear 1s;
transition: background-color linear 1s;
}
/*.im_message_focus.inactive {
background-color: none;
}*/
.im_history_selectable .im_message_outer_wrap { .im_history_selectable .im_message_outer_wrap {
cursor: pointer; cursor: pointer;
} }
@ -3103,7 +3143,8 @@ a.contacts_modal_contact:hover .contacts_modal_contact_status {
margin-top: 1px; margin-top: 1px;
border: 0; border: 0;
} }
.im_message_grouped .icon-message-status { .im_message_grouped1 .icon-message-status,
.im_message_grouped2 .icon-message-status {
margin-top: -8px; margin-top: -8px;
} }
} }

268
app/js/controllers.js

@ -216,10 +216,10 @@ angular.module('myApp.controllers', [])
$scope.$on('history_focus', function (e, peerData) { $scope.$on('history_focus', function (e, peerData) {
$modalStack.dismissAll(); $modalStack.dismissAll();
if (peerData.peerString == $scope.curDialog.peer) { if (peerData.peerString == $scope.curDialog.peer && !peerData.messageID) {
$scope.$broadcast('ui_history_focus'); $scope.$broadcast('ui_history_focus');
} else { } else {
$location.url('/im?p=' + peerData.peerString); $location.url('/im?p=' + peerData.peerString + (peerData.messageID ? '&m=' + peerData.messageID : ''));
} }
}); });
@ -261,8 +261,12 @@ angular.module('myApp.controllers', [])
}); });
} }
$scope.dialogSelect = function (peerString) { $scope.dialogSelect = function (peerString, messageID) {
$rootScope.$broadcast('history_focus', {peerString: peerString}); var params = {peerString: peerString};
if (messageID) {
params.messageID = messageID;
}
$rootScope.$broadcast('history_focus', params);
}; };
$scope.logOut = function () { $scope.logOut = function () {
@ -278,16 +282,15 @@ angular.module('myApp.controllers', [])
function updateCurDialog() { function updateCurDialog() {
$scope.curDialog = { $scope.curDialog = {
peer: $routeParams.p || false peer: $routeParams.p || false,
messageID: $routeParams.m || false
}; };
} }
ChangelogNotifyService.checkUpdate(); ChangelogNotifyService.checkUpdate();
}) })
.controller('AppImDialogsController', function ($scope, $location, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, PhonebookContactsService, ErrorService) { .controller('AppImDialogsController', function ($scope, $location, $q, $timeout, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, PhonebookContactsService, ErrorService) {
// console.log('init controller');
$scope.dialogs = []; $scope.dialogs = [];
$scope.contacts = []; $scope.contacts = [];
@ -298,6 +301,7 @@ angular.module('myApp.controllers', [])
var offset = 0, var offset = 0,
maxID = 0, maxID = 0,
hasMore = false, hasMore = false,
jump = 0,
peersInDialogs = {}, peersInDialogs = {},
contactsShown; contactsShown;
@ -345,7 +349,84 @@ angular.module('myApp.controllers', [])
} }
}); });
$scope.$watch('search.query', loadDialogs); $scope.$watchCollection('search', loadDialogs);
$scope.importContact = function () {
AppUsersManager.openImportContact().then(function (foundContact) {
if (contactsShown && foundContact) {
loadDialogs();
}
});
};
$scope.importPhonebook = function () {
PhonebookContactsService.openPhonebookImport().result.then(function (foundContacts) {
if (contactsShown && foundContacts.length) {
loadDialogs();
}
})
};
var searchTimeoutPromise;
function getDialogs() {
var searchMessages = $scope.search.messages && $scope.search.query.length > 0,
curJump = ++jump,
promise;
$timeout.cancel(searchTimeoutPromise);
if (searchMessages) {
searchTimeoutPromise = $timeout(angular.noop, 500);
promise = searchTimeoutPromise.then(function () {
return AppMessagesManager.getSearch({_: 'inputPeerEmpty'}, $scope.search.query, {_: 'inputMessagesFilterEmpty'}, maxID)
});
} else {
promise = AppMessagesManager.getDialogs($scope.search.query, maxID);
}
return promise.then(function (result) {
if (curJump != jump) {
return $q.reject();
}
if (searchMessages) {
var dialogs = [];
angular.forEach(result.history, function (messageID) {
var message = AppMessagesManager.getMessage(messageID),
peerID = AppMessagesManager.getMessagePeer(message);
dialogs.push({
peerID: peerID,
top_message: messageID,
unread_count: 0
});
});
result = {
count: result.count,
dialogs: dialogs
};
}
return result;
}, function (error) {
if (error.type == 'NETWORK_BAD_REQUEST') {
if (location.protocol == 'https:') {
ErrorService.confirm({type: 'HTTPS_MIXED_FAIL'}).then(function () {
location = location.toString().replace(/^https:/, 'http:');
});
error.handled = true;
}
}
if (error.code == 401) {
MtpApiManager.logOut()['finally'](function () {
$location.url('/login');
});
error.handled = true;
}
return $q.reject();
});
};
$scope.importContact = function () { $scope.importContact = function () {
AppUsersManager.openImportContact().then(function (foundContact) { AppUsersManager.openImportContact().then(function (foundContact) {
@ -370,7 +451,7 @@ angular.module('myApp.controllers', [])
peersInDialogs = {}; peersInDialogs = {};
contactsShown = false; contactsShown = false;
AppMessagesManager.getDialogs($scope.search.query, maxID).then(function (dialogsResult) { getDialogs().then(function (dialogsResult) {
$scope.dialogs = []; $scope.dialogs = [];
$scope.contacts = []; $scope.contacts = [];
@ -398,22 +479,6 @@ angular.module('myApp.controllers', [])
showMoreDialogs(); showMoreDialogs();
} }
}, function (error) {
if (error.type == 'NETWORK_BAD_REQUEST') {
if (location.protocol == 'https:') {
ErrorService.confirm({type: 'HTTPS_MIXED_FAIL'}).then(function () {
location = location.toString().replace(/^https:/, 'http:');
});
error.handled = true;
}
}
if (error.code == 401) {
MtpApiManager.logOut()['finally'](function () {
$location.url('/login');
});
error.handled = true;
}
}); });
} }
@ -422,10 +487,12 @@ angular.module('myApp.controllers', [])
return; return;
} }
if (!hasMore && ($scope.search.query || !$scope.dialogs.length)) { if (!hasMore && !$scope.search.messages && ($scope.search.query || !$scope.dialogs.length)) {
contactsShown = true; contactsShown = true;
var curJump = ++jump;
AppUsersManager.getContacts($scope.search.query).then(function (contactsList) { AppUsersManager.getContacts($scope.search.query).then(function (contactsList) {
if (curJump != jump) return;
$scope.contacts = []; $scope.contacts = [];
angular.forEach(contactsList, function(userID) { angular.forEach(contactsList, function(userID) {
if (peersInDialogs[userID] === undefined) { if (peersInDialogs[userID] === undefined) {
@ -448,7 +515,7 @@ angular.module('myApp.controllers', [])
return; return;
} }
AppMessagesManager.getDialogs($scope.search.query, maxID).then(function (dialogsResult) { getDialogs().then(function (dialogsResult) {
if (dialogsResult.dialogs.length) { if (dialogsResult.dialogs.length) {
offset += dialogsResult.dialogs.length; offset += dialogsResult.dialogs.length;
maxID = dialogsResult.dialogs[dialogsResult.dialogs.length - 1].top_message; maxID = dialogsResult.dialogs[dialogsResult.dialogs.length - 1].top_message;
@ -468,10 +535,9 @@ angular.module('myApp.controllers', [])
.controller('AppImHistoryController', function ($scope, $location, $timeout, $rootScope, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager, PeersSelectService, IdleManager, StatusManager, ErrorService) { .controller('AppImHistoryController', function ($scope, $location, $timeout, $rootScope, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager, PeersSelectService, IdleManager, StatusManager, ErrorService) {
$scope.$watch('curDialog.peer', applyDialogSelect); $scope.$watch('curDialog', applyDialogSelect);
ApiUpdatesManager.attach(); ApiUpdatesManager.attach();
IdleManager.start(); IdleManager.start();
StatusManager.start(); StatusManager.start();
@ -494,9 +560,10 @@ angular.module('myApp.controllers', [])
$scope.showPeerInfo = showPeerInfo; $scope.showPeerInfo = showPeerInfo;
var peerID, var peerID,
offset = 0,
hasMore = false, hasMore = false,
hasLess = false,
maxID = 0, maxID = 0,
minID = 0,
lastSelectID = false, lastSelectID = false,
inputMediaFilters = { inputMediaFilters = {
photos: 'inputMessagesFilterPhotos', photos: 'inputMessagesFilterPhotos',
@ -504,22 +571,32 @@ angular.module('myApp.controllers', [])
documents: 'inputMessagesFilterDocument', documents: 'inputMessagesFilterDocument',
audio: 'inputMessagesFilterAudio' audio: 'inputMessagesFilterAudio'
}, },
jump = 0; jump = 0,
moreJump = 0,
function applyDialogSelect (newPeer) { lessJump = 0;
selectedCancel(true);
newPeer = newPeer || $scope.curDialog.peer || '';
function applyDialogSelect (newDialog, oldDialog) {
var newPeer = newDialog.peer || $scope.curDialog.peer || '';
peerID = AppPeersManager.getPeerID(newPeer); peerID = AppPeersManager.getPeerID(newPeer);
if (peerID == $scope.curDialog.peerID && oldDialog.messageID == newDialog.messageID) {
return false;
}
$scope.curDialog.peerID = peerID; $scope.curDialog.peerID = peerID;
$scope.curDialog.inputPeer = AppPeersManager.getInputPeer(newPeer); $scope.curDialog.inputPeer = AppPeersManager.getInputPeer(newPeer);
$scope.mediaType = false; $scope.mediaType = false;
if (peerID) { selectedCancel(true);
if (oldDialog.peer && oldDialog.peer == newDialog.peer && newDialog.messageID) {
messageFocusHistory();
}
else if (peerID) {
updateHistoryPeer(true); updateHistoryPeer(true);
loadHistory(); loadHistory();
} else { }
else {
showEmptyHistory(); showEmptyHistory();
} }
} }
@ -581,56 +658,119 @@ angular.module('myApp.controllers', [])
} }
} }
function messageFocusHistory () {
var i, found = false;
for (i = 0; i < $scope.history.length; i++) {
if ($scope.curDialog.messageID == $scope.history[i].id) {
found = true;
break;
}
}
if (found) {
$scope.historyUnread = {};
$scope.historyFocus = $scope.curDialog.messageID;
$scope.$broadcast('ui_history_change_scroll');
} else {
loadHistory();
}
}
function showLessHistory () {
if (!hasLess) {
return;
}
var curJump = jump,
curLessJump = ++lessJump,
limit = 0,
backLimit = 20;
AppMessagesManager.getHistory($scope.curDialog.inputPeer, minID, limit, backLimit).then(function (historyResult) {
if (curJump != jump || curLessJump != lessJump) return;
angular.forEach(historyResult.history, function (id) {
if (id > minID) {
$scope.history.push(AppMessagesManager.wrapForHistory(id));
}
});
if (historyResult.history.length) {
minID = historyResult.history.length >= backLimit
? historyResult.history[0]
: 0;
updateHistoryGroups(-backLimit);
$scope.$broadcast('ui_history_append');
} else {
minID = 0;
}
hasLess = minID > 0;
});
}
function showMoreHistory () { function showMoreHistory () {
if (!hasMore || !offset) { if (!hasMore) {
return; return;
} }
// console.trace('load history');
var curJump = ++jump, var curJump = jump,
curMoreJump = moreJump,
inputMediaFilter = $scope.mediaType && {_: inputMediaFilters[$scope.mediaType]}, inputMediaFilter = $scope.mediaType && {_: inputMediaFilters[$scope.mediaType]},
getMessagesPromise = inputMediaFilter getMessagesPromise = inputMediaFilter
? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID) ? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID)
: AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID); : AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID);
getMessagesPromise.then(function (historyResult) { getMessagesPromise.then(function (historyResult) {
if (curJump != jump) return; if (curJump != jump || curMoreJump != moreJump) return;
offset += historyResult.history.length;
hasMore = historyResult.count === null || offset < historyResult.count;
maxID = historyResult.history[historyResult.history.length - 1];
angular.forEach(historyResult.history, function (id) { angular.forEach(historyResult.history, function (id) {
$scope.history.unshift(AppMessagesManager.wrapForHistory(id)); $scope.history.unshift(AppMessagesManager.wrapForHistory(id));
}); });
updateHistoryGroups(historyResult.history.length + 1); hasMore = historyResult.count === null ||
historyResult.history.length && $scope.history.length < historyResult.count;
$scope.$broadcast('ui_history_prepend'); if (historyResult.history.length) {
maxID = historyResult.history[historyResult.history.length - 1];
updateHistoryGroups(historyResult.history.length + 1);
$scope.$broadcast('ui_history_prepend');
}
}); });
} };
function loadHistory () { function loadHistory () {
hasMore = false; hasMore = false;
offset = 0; hasLess = false;
maxID = 0; maxID = 0;
minID = 0;
var limit = 0, backLimit = 0;
if ($scope.curDialog.messageID) {
maxID = parseInt($scope.curDialog.messageID);
limit = 5;
backLimit = 5;
}
var curJump = ++jump, var curJump = ++jump,
inputMediaFilter = $scope.mediaType && {_: inputMediaFilters[$scope.mediaType]}, inputMediaFilter = $scope.mediaType && {_: inputMediaFilters[$scope.mediaType]},
getMessagesPromise = inputMediaFilter getMessagesPromise = inputMediaFilter
? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID) ? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID)
: AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID); : AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID, limit, backLimit);
$scope.state.mayBeHasMore = true; $scope.state.mayBeHasMore = true;
getMessagesPromise.then(function (historyResult) { getMessagesPromise.then(function (historyResult) {
if (curJump != jump) return; if (curJump != jump) return;
offset += historyResult.history.length; minID = maxID && historyResult.history.indexOf(maxID)>= backLimit - 1
? historyResult.history[0]
hasMore = historyResult.count === null || offset < historyResult.count; : 0;
maxID = historyResult.history[historyResult.history.length - 1]; maxID = historyResult.history[historyResult.history.length - 1];
hasLess = minID > 0;
hasMore = historyResult.count === null ||
historyResult.history.length && historyResult.history.length < historyResult.count;
updateHistoryPeer(); updateHistoryPeer();
safeReplaceObject($scope.state, {loaded: true}); safeReplaceObject($scope.state, {loaded: true});
@ -651,6 +791,8 @@ angular.module('myApp.controllers', [])
$scope.historyUnread = {}; $scope.historyUnread = {};
} }
$scope.historyFocus = $scope.curDialog.messageID || 0;
$scope.$broadcast('ui_history_change'); $scope.$broadcast('ui_history_change');
AppMessagesManager.readHistory($scope.curDialog.inputPeer); AppMessagesManager.readHistory($scope.curDialog.inputPeer);
@ -805,6 +947,14 @@ angular.module('myApp.controllers', [])
$scope.$on('history_append', function (e, addedMessage) { $scope.$on('history_append', function (e, addedMessage) {
if (addedMessage.peerID == $scope.curDialog.peerID) { if (addedMessage.peerID == $scope.curDialog.peerID) {
if (minID) {
if (addedMessage.my) {
$rootScope.$broadcast('history_focus', {peerString: $scope.curDialog.peer});
} else {
$scope.missedCount++;
}
return;
}
if ($scope.mediaType) { if ($scope.mediaType) {
if (addedMessage.my) { if (addedMessage.my) {
toggleMedia(); toggleMedia();
@ -818,7 +968,7 @@ angular.module('myApp.controllers', [])
$scope.history.push(AppMessagesManager.wrapForHistory(addedMessage.messageID)); $scope.history.push(AppMessagesManager.wrapForHistory(addedMessage.messageID));
updateHistoryGroups(-3); updateHistoryGroups(-3);
$scope.typing = {}; $scope.typing = {};
$scope.$broadcast('ui_history_append', {my: addedMessage.my}); $scope.$broadcast('ui_history_append_new', {my: addedMessage.my});
if (addedMessage.my) { if (addedMessage.my) {
$scope.historyUnread = {}; $scope.historyUnread = {};
} }
@ -889,9 +1039,8 @@ angular.module('myApp.controllers', [])
} }
}); });
$scope.$on('history_need_more', function () { $scope.$on('history_need_less', showLessHistory);
showMoreHistory(); $scope.$on('history_need_more', showMoreHistory);
});
$rootScope.$watch('idle.isIDLE', function (newVal) { $rootScope.$watch('idle.isIDLE', function (newVal) {
if (!newVal && $scope.curDialog && $scope.curDialog.peerID) { if (!newVal && $scope.curDialog && $scope.curDialog.peerID) {
@ -976,9 +1125,10 @@ angular.module('myApp.controllers', [])
function onMessageChange(newVal) { function onMessageChange(newVal) {
// console.log('ctrl text changed', newVal); // console.log('ctrl text changed', newVal);
// console.trace('ctrl text changed', newVal); // console.trace('ctrl text changed', newVal);
AppMessagesManager.readHistory($scope.curDialog.inputPeer);
if (newVal && newVal.length) { if (newVal && newVal.length) {
AppMessagesManager.readHistory($scope.curDialog.inputPeer);
var backupDraftObj = {}; var backupDraftObj = {};
backupDraftObj['draft' + $scope.curDialog.peerID] = newVal; backupDraftObj['draft' + $scope.curDialog.peerID] = newVal;
AppConfigManager.set(backupDraftObj); AppConfigManager.set(backupDraftObj);

84
app/js/directives.js

@ -329,7 +329,8 @@ angular.module('myApp.directives', ['myApp.filters'])
headWrap = $('.tg_page_head')[0], headWrap = $('.tg_page_head')[0],
footer = $('.im_page_footer')[0], footer = $('.im_page_footer')[0],
sendForm = $('.im_send_form', element)[0], sendForm = $('.im_send_form', element)[0],
moreNotified = false; moreNotified = false,
lessNotified = false;
onContentLoaded(function () { onContentLoaded(function () {
scrollableWrap.scrollTop = scrollableWrap.scrollHeight; scrollableWrap.scrollTop = scrollableWrap.scrollHeight;
@ -337,6 +338,7 @@ angular.module('myApp.directives', ['myApp.filters'])
}); });
var updateScroller = function (delay) { var updateScroller = function (delay) {
// console.trace('scroller update', delay);
$timeout(function () { $timeout(function () {
if (!$(scrollableWrap).hasClass('im_history_to_bottom')) { if (!$(scrollableWrap).hasClass('im_history_to_bottom')) {
$(historyWrap).nanoScroller(); $(historyWrap).nanoScroller();
@ -357,7 +359,7 @@ angular.module('myApp.directives', ['myApp.filters'])
var animated = transform ? true : false, var animated = transform ? true : false,
curAnimation = false; curAnimation = false;
$scope.$on('ui_history_append', function (e, options) { $scope.$on('ui_history_append_new', function (e, options) {
if (!atBottom && !options.my) { if (!atBottom && !options.my) {
return; return;
} }
@ -392,6 +394,24 @@ angular.module('myApp.directives', ['myApp.filters'])
}); });
}); });
function changeScroll () {
var unreadSplit, focusMessage;
if (focusMessage = $('.im_message_focus', scrollableWrap)[0]) {
scrollableWrap.scrollTop = Math.max(0, focusMessage.offsetTop - 52);
atBottom = false;
} else if (unreadSplit = $('.im_message_unread_split', scrollableWrap)[0]) {
scrollableWrap.scrollTop = Math.max(0, unreadSplit.offsetTop - 52);
atBottom = false;
} else {
scrollableWrap.scrollTop = scrollableWrap.scrollHeight;
}
updateScroller();
$timeout(function () {
$(scrollableWrap).trigger('scroll');
});
};
$scope.$on('ui_history_change', function () { $scope.$on('ui_history_change', function () {
$(scrollableWrap).addClass('im_history_to_bottom'); $(scrollableWrap).addClass('im_history_to_bottom');
$(scrollable).css({bottom: 0}); $(scrollable).css({bottom: 0});
@ -399,24 +419,16 @@ angular.module('myApp.directives', ['myApp.filters'])
$(scrollableWrap).removeClass('im_history_to_bottom'); $(scrollableWrap).removeClass('im_history_to_bottom');
$(scrollable).css({bottom: ''}); $(scrollable).css({bottom: ''});
updateSizes(true); updateSizes(true);
var unreadSplit = $('.im_message_unread_split', scrollableWrap);
if (unreadSplit[0]) {
scrollableWrap.scrollTop = Math.max(0, unreadSplit[0].offsetTop - 52);
atBottom = false;
} else {
scrollableWrap.scrollTop = scrollableWrap.scrollHeight;
}
updateScroller();
moreNotified = false; moreNotified = false;
lessNotified = false;
$timeout(function () { changeScroll();
$(scrollableWrap).trigger('scroll');
});
}); });
}); });
$scope.$on('ui_history_change_scroll', function () {
onContentLoaded(changeScroll)
});
$scope.$on('ui_history_focus', function () { $scope.$on('ui_history_focus', function () {
if (!atBottom) { if (!atBottom) {
scrollableWrap.scrollTop = scrollableWrap.scrollHeight; scrollableWrap.scrollTop = scrollableWrap.scrollHeight;
@ -450,6 +462,20 @@ angular.module('myApp.directives', ['myApp.filters'])
}); });
}); });
$scope.$on('ui_history_append', function () {
var sh = scrollableWrap.scrollHeight;
onContentLoaded(function () {
updateBottomizer();
lessNotified = false;
$timeout(function () {
if (scrollableWrap.scrollHeight != sh) {
$(scrollableWrap).trigger('scroll');
}
});
});
});
$scope.$on('ui_panel_update', function () { $scope.$on('ui_panel_update', function () {
onContentLoaded(function () { onContentLoaded(function () {
updateSizes(); updateSizes();
@ -493,6 +519,10 @@ angular.module('myApp.directives', ['myApp.filters'])
moreNotified = true; moreNotified = true;
$scope.$emit('history_need_more'); $scope.$emit('history_need_more');
} }
else if (!lessNotified && scrollableWrap.scrollTop >= scrollableWrap.scrollHeight - scrollableWrap.clientHeight - 300) {
lessNotified = true;
$scope.$emit('history_need_less');
}
}); });
function updateSizes (heightOnly) { function updateSizes (heightOnly) {
@ -623,6 +653,7 @@ angular.module('myApp.directives', ['myApp.filters'])
if (submit) { if (submit) {
$(element).trigger('submit'); $(element).trigger('submit');
$(element).trigger('message_send'); $(element).trigger('message_send');
resetAfterSubmit();
return cancelEvent(e); return cancelEvent(e);
} }
} }
@ -632,19 +663,28 @@ angular.module('myApp.directives', ['myApp.filters'])
$('.im_submit', element).on('mousedown', function (e) { $('.im_submit', element).on('mousedown', function (e) {
$(element).trigger('submit'); $(element).trigger('submit');
$(element).trigger('message_send'); $(element).trigger('message_send');
resetAfterSubmit();
return cancelEvent(e); return cancelEvent(e);
}); });
var lastTyping = 0; var lastTyping = 0,
lastLength;
$(editorElement).on('keyup', function (e) { $(editorElement).on('keyup', function (e) {
var now = tsNow(); var now = tsNow(),
if (now - lastTyping < 5000) { length = (editorElement[richTextarea ? 'innerText' : 'value']).length;
return;
if (now - lastTyping > 5000 && length != lastLength) {
lastTyping = now;
lastLength = length;
$scope.$emit('ui_typing');
} }
lastTyping = now;
$scope.$emit('ui_typing');
}); });
function resetAfterSubmit () {
lastTyping = 0;
lastLength = 0;
};
function updateField () { function updateField () {
if (richTextarea) { if (richTextarea) {
$timeout.cancel(updatePromise); $timeout.cancel(updatePromise);

2
app/js/init.js

@ -22,7 +22,7 @@
setTimeout(function () {callback(result)}, 10); setTimeout(function () {callback(result)}, 10);
}; };
if (!window.applicationCache || Config.App.packaged || !window.addEventListener) { if (!window.applicationCache || Config.Modes.packed || !window.addEventListener) {
return; return;
} }

11
app/js/lib/config.js

File diff suppressed because one or more lines are too long

2
app/js/lib/mtproto.js

@ -1785,7 +1785,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
var serializer = new TLSerialization(options); var serializer = new TLSerialization(options);
if (!this.connectionInited) { if (!this.connectionInited) {
serializer.storeInt(0x2b9b08fa, 'invokeWithLayer14'); serializer.storeInt(0xb4418b64, 'invokeWithLayer15');
serializer.storeInt(0x69796de9, 'initConnection'); serializer.storeInt(0x69796de9, 'initConnection');
serializer.storeInt(Config.App.id, 'api_id'); serializer.storeInt(Config.App.id, 'api_id');
serializer.storeString(navigator.userAgent || 'Unknown UserAgent', 'device_model'); serializer.storeString(navigator.userAgent || 'Unknown UserAgent', 'device_model');

76
app/js/services.js

@ -750,7 +750,6 @@ angular.module('myApp.services', [])
} }
function search (query, searchIndex) { function search (query, searchIndex) {
console.time('search');
var shortIndexes = searchIndex.shortIndexes, var shortIndexes = searchIndex.shortIndexes,
fullTexts = searchIndex.fullTexts; fullTexts = searchIndex.fullTexts;
@ -787,7 +786,6 @@ angular.module('myApp.services', [])
} }
} }
console.timeEnd('search');
return newFoundObjs; return newFoundObjs;
} }
}) })
@ -873,9 +871,7 @@ angular.module('myApp.services', [])
} }
} }
curDialogStorage.count = dialogsResult._ == 'messages.dialogsSlice' curDialogStorage.count = dialogsResult.count || dialogsResult.dialogs.length;
? dialogsResult.count
: dialogsResult.dialogs.length;
curDialogStorage.dialogs.splice(offset, curDialogStorage.dialogs.length - offset); curDialogStorage.dialogs.splice(offset, curDialogStorage.dialogs.length - offset);
angular.forEach(dialogsResult.dialogs, function (dialog) { angular.forEach(dialogsResult.dialogs, function (dialog) {
@ -904,21 +900,29 @@ angular.module('myApp.services', [])
}); });
} }
function fillHistoryStorage (inputPeer, maxID, fullLimit, historyStorage) { function requestHistory (inputPeer, maxID, limit, backLimit) {
console.log('fill history storage', inputPeer, maxID, fullLimit, angular.copy(historyStorage)); maxID = maxID || 0;
limit = limit || 0;
backLimit = backLimit || 0;
return MtpApiManager.invokeApi('messages.getHistory', { return MtpApiManager.invokeApi('messages.getHistory', {
peer: inputPeer, peer: inputPeer,
offset: 0, offset: -backLimit,
limit: fullLimit, limit: limit + backLimit,
max_id: maxID || 0 max_id: maxID || 0
}).then(function (historyResult) { }).then(function (historyResult) {
AppUsersManager.saveApiUsers(historyResult.users); AppUsersManager.saveApiUsers(historyResult.users);
AppChatsManager.saveApiChats(historyResult.chats); AppChatsManager.saveApiChats(historyResult.chats);
saveMessages(historyResult.messages); saveMessages(historyResult.messages);
historyStorage.count = historyResult._ == 'messages.messagesSlice' return historyResult;
? historyResult.count });
: historyResult.messages.length; }
function fillHistoryStorage (inputPeer, maxID, fullLimit, historyStorage) {
console.log('fill history storage', inputPeer, maxID, fullLimit, angular.copy(historyStorage));
return requestHistory (inputPeer, maxID, fullLimit).then(function (historyResult) {
historyStorage.count = historyResult.count || historyResult.messages.length;
var offset = 0; var offset = 0;
if (maxID > 0) { if (maxID > 0) {
@ -945,8 +949,7 @@ angular.module('myApp.services', [])
}); });
}; };
function getHistory (inputPeer, maxID, limit) { function getHistory (inputPeer, maxID, limit, backLimit) {
var peerID = AppPeersManager.getPeerID(inputPeer), var peerID = AppPeersManager.getPeerID(inputPeer),
historyStorage = historiesStorage[peerID], historyStorage = historiesStorage[peerID],
offset = 0, offset = 0,
@ -979,9 +982,17 @@ angular.module('myApp.services', [])
if (historyStorage.count !== null && historyStorage.history.length == historyStorage.count || if (historyStorage.count !== null && historyStorage.history.length == historyStorage.count ||
historyStorage.history.length >= offset + (limit || 1) historyStorage.history.length >= offset + (limit || 1)
) { ) {
if (backLimit) {
backLimit = Math.min(offset, backLimit);
offset = Math.max(0, offset - backLimit);
limit += backLimit;
} else {
limit = limit || 20;
}
return $q.when({ return $q.when({
count: historyStorage.count, count: historyStorage.count,
history: resultPending.concat(historyStorage.history.slice(offset, offset + (limit || 20))), history: resultPending.concat(historyStorage.history.slice(offset, offset + limit)),
unreadLimit: unreadLimit unreadLimit: unreadLimit
}); });
} }
@ -989,8 +1000,26 @@ angular.module('myApp.services', [])
if (unreadLimit) { if (unreadLimit) {
limit = Math.max(20, unreadLimit + 2); limit = Math.max(20, unreadLimit + 2);
} }
else if (!backLimit && !limit) {
limit = 20;
}
limit = limit || 20; if (backLimit || maxID && historyStorage.history.indexOf(maxID) == -1) {
return requestHistory(inputPeer, maxID, limit, backLimit).then(function (historyResult) {
historyStorage.count = historyResult.count || historyResult.messages.length;
var history = [];
angular.forEach(historyResult.messages, function (message) {
history.push(message.id);
});
return {
count: historyStorage.count,
history: resultPending.concat(history),
unreadLimit: unreadLimit
};
})
}
return fillHistoryStorage(inputPeer, maxID, limit, historyStorage).then(function () { return fillHistoryStorage(inputPeer, maxID, limit, historyStorage).then(function () {
offset = 0; offset = 0;
@ -1102,9 +1131,7 @@ angular.module('myApp.services', [])
AppChatsManager.saveApiChats(searchResult.chats); AppChatsManager.saveApiChats(searchResult.chats);
saveMessages(searchResult.messages); saveMessages(searchResult.messages);
var foundCount = searchResult._ == 'messages.messagesSlice' var foundCount = searchResult.count || searchResult.messages.length;
? searchResult.count
: searchResult.messages.length;
foundMsgs = []; foundMsgs = [];
angular.forEach(searchResult.messages, function (message) { angular.forEach(searchResult.messages, function (message) {
@ -1186,6 +1213,10 @@ angular.module('myApp.services', [])
} }
} }
if (historyStorage.readPromise) {
return historyStorage.readPromise;
}
var promise = MtpApiManager.invokeApi('messages.readHistory', { var promise = MtpApiManager.invokeApi('messages.readHistory', {
peer: inputPeer, peer: inputPeer,
offset: 0, offset: 0,
@ -1198,6 +1229,8 @@ angular.module('myApp.services', [])
foundDialog[0].unread_count = 0; foundDialog[0].unread_count = 0;
$rootScope.$broadcast('dialog_unread', {peerID: peerID, count: 0}); $rootScope.$broadcast('dialog_unread', {peerID: peerID, count: 0});
} }
})['finally'](function () {
delete historyStorage.readPromise;
}); });
@ -3125,9 +3158,10 @@ angular.module('myApp.services', [])
videoID = youtubeMatches && youtubeMatches[1]; videoID = youtubeMatches && youtubeMatches[1];
if (videoID) { if (videoID) {
text = text + '<div class="im_message_iframe_video"><iframe type="text/html" frameborder="0" ' + var tag = Config.Modes.chrome_packed ? 'webview' : 'iframe';
text = text + '<div class="im_message_iframe_video"><' + tag + ' type="text/html" frameborder="0" ' +
'src="http://www.youtube.com/embed/' + videoID + 'src="http://www.youtube.com/embed/' + videoID +
'?autoplay=0&amp;controls=2"></iframe></div>' '?autoplay=0&amp;controls=2"></' + tag + '></div>'
} }
} }

4
app/partials/dialog.html

@ -1,4 +1,4 @@
<a class="im_dialog" ng-click="dialogSelect(dialogMessage.peerString)"> <a class="im_dialog" ng-click="dialogSelect(dialogMessage.peerString, search.messages &amp;&amp; dialogMessage.id)">
<div class="im_dialog_meta pull-right text-right"> <div class="im_dialog_meta pull-right text-right">
<div class="im_dialog_date" ng-bind="dialogMessage.dateText"></div> <div class="im_dialog_date" ng-bind="dialogMessage.dateText"></div>
@ -52,7 +52,7 @@
</span> </span>
<span class="im_dialog_message_service" ng-if="dialogMessage._ == 'messageService'" ng-switch="dialogMessage.action._"> <span class="im_dialog_message_service" ng-if="dialogMessage._ == 'messageService'" ng-switch="dialogMessage.action._">
<span ng-switch-when="messageActionChatCreate"> created the group </span> <span ng-switch-when="messageActionChatCreate">created the group</span>
<span ng-switch-when="messageActionChatEditTitle">changed group name</span> <span ng-switch-when="messageActionChatEditTitle">changed group name</span>
<span ng-switch-when="messageActionChatEditPhoto">changed group photo</span> <span ng-switch-when="messageActionChatEditPhoto">changed group photo</span>
<span ng-switch-when="messageActionChatDeletePhoto">removed group photo</span> <span ng-switch-when="messageActionChatDeletePhoto">removed group photo</span>

19
app/partials/im.html

@ -23,7 +23,15 @@
<input class="form-control im_dialogs_search_field" type="search" placeholder="Search" ng-model="search.query"/> <input class="form-control im_dialogs_search_field" type="search" placeholder="Search" ng-model="search.query"/>
<a class="im_dialogs_search_clear" ng-click="search.query = ''" ng-show="search.query.length"></a> <a class="im_dialogs_search_clear" ng-click="search.query = ''" ng-show="search.query.length"></a>
</div> </div>
<div class="im_dialogs_tabs_wrap" ng-show="search.query.length">
<div class="im_dialogs_tabs clearfix">
<a href="" class="im_dialogs_tab" ng-class="{active: !search.messages}" ng-click="search.messages = false">Conversations</a>
<a href="" class="im_dialogs_tab" ng-class="{active: search.messages}" ng-click="search.messages = true">Messages</a>
</div>
</div>
</div> </div>
<div my-dialogs-list class="im_dialogs_col"> <div my-dialogs-list class="im_dialogs_col">
<div class="im_dialogs_wrap nano"> <div class="im_dialogs_wrap nano">
<div class="im_dialogs_scrollable_wrap content"> <div class="im_dialogs_scrollable_wrap content">
@ -35,9 +43,14 @@
<button ng-if="phonebookAvailable" type="button" class="btn btn-primary btn-sm im_dialogs_import_phonebook" ng-click="importPhonebook()">Import phonebook</button> <button ng-if="phonebookAvailable" type="button" class="btn btn-primary btn-sm im_dialogs_import_phonebook" ng-click="importPhonebook()">Import phonebook</button>
</div> </div>
<ul class="nav nav-pills nav-stacked"> <div ng-switch="search.messages">
<li class="im_dialog_wrap" my-dialog dialog-message="dialogMessage" ng-repeat="dialogMessage in dialogs track by dialogMessage.peerID" ng-class="{active: curDialog.peerID == dialogMessage.peerID}"></li> <ul ng-switch-when="true" class="nav nav-pills nav-stacked">
</ul> <li class="im_dialog_wrap" my-dialog dialog-message="dialogMessage" ng-repeat="dialogMessage in dialogs track by dialogMessage.id" ng-class="{active: curDialog.peerID == dialogMessage.peerID &amp;&amp; curDialog.messageID == dialogMessage.id}"></li>
</ul>
<ul ng-switch-default class="nav nav-pills nav-stacked">
<li class="im_dialog_wrap" my-dialog dialog-message="dialogMessage" ng-repeat="dialogMessage in dialogs track by dialogMessage.peerID" ng-class="{active: curDialog.peerID == dialogMessage.peerID}"></li>
</ul>
</div>
<div class="im_dialogs_contacts_wrap" ng-show="contacts.length > 0"> <div class="im_dialogs_contacts_wrap" ng-show="contacts.length > 0">
<h5>Contacts</h5> <h5>Contacts</h5>

2
app/partials/message.html

@ -4,7 +4,7 @@
</ng-pluralize> </ng-pluralize>
</div> </div>
<div class="im_message_outer_wrap" ng-class="[ selectedMsgs[historyMessage.id] &amp;&amp; 'im_message_selected', historyMessage.grouped &amp;&amp; ('im_message_grouped' + historyMessage.grouped) ]" ng-click="toggleMessage(historyMessage.id, $event)"> <div class="im_message_outer_wrap" ng-class="[ selectedMsgs[historyMessage.id] &amp;&amp; 'im_message_selected', historyMessage.grouped &amp;&amp; ('im_message_grouped' + historyMessage.grouped) , historyFocus == historyMessage.id &amp;&amp; 'im_message_focus']" ng-click="toggleMessage(historyMessage.id, $event)">
<div class="im_message_wrap clearfix" bindonce> <div class="im_message_wrap clearfix" bindonce>

Loading…
Cancel
Save