From f8ff892ccc11393df723611869d3c7c874bd5bde Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Wed, 12 Feb 2014 19:10:05 +0400 Subject: [PATCH] Added basic contacts modal, improved read messages handling, improved mobile landscape experience --- app/css/app.css | 127 ++++++++++++++++-- app/js/background.js | 2 +- app/js/controllers.js | 37 ++++- app/js/directives.js | 19 ++- app/js/services.js | 102 +++++++++++--- app/partials/chat_modal.html | 4 +- app/partials/contacts_modal.html | 36 +++++ app/partials/head.html | 3 +- app/partials/im.html | 2 +- .../ui-bootstrap-custom-tpls-0.10.0.js | 4 +- 10 files changed, 294 insertions(+), 42 deletions(-) create mode 100644 app/partials/contacts_modal.html diff --git a/app/css/app.css b/app/css/app.css index d7972d10..402b5503 100644 --- a/app/css/app.css +++ b/app/css/app.css @@ -444,7 +444,6 @@ fieldset[disabled] .btn-tg.active { .is_1x .im_dialogs_search_field { background-image: url(../img/icons/IconsetW_1x.png?2); } - .im_dialogs_search_field:focus, .im_dialogs_search_field:active { background-color: #FFF; @@ -617,8 +616,8 @@ a.im_dialog:hover .im_dialog_date { } .im_history_col .nano > .pane { - /*background : rgba(0,0,0,.0);*/ - background: #E9EBED; + background : rgba(3,36,64,0.08); + /*background: #E9EBED;*/ width : 9px; top: 10px; /*margin-top: 10px;*/ @@ -632,7 +631,8 @@ a.im_dialog:hover .im_dialog_date { border-radius : 2px; } .im_history_col .nano > .pane > .slider { - background: #B3BFC7; + background : rgba(3,46,79,0.22); + /*background: #B3BFC7;*/ margin: 0; -moz-border-radius : 2px; -webkit-border-radius : 2px; @@ -1271,7 +1271,7 @@ div.im_panel_own_photo { margin-top: -7px; margin-left: 43px; } -.modal_participant_online { +.status_online { background: #6DBF69; border: 1px solid #FFF; display: block; @@ -1577,12 +1577,10 @@ img.img_fullsize { max-width: auto; box-shadow: none; -webkit-box-shadow: none; - margin: 30px auto 10px; + margin: 30px auto 20px; } - .im_page_footer { - display: none; - } + .im_panel_own_photo, .im_panel_peer_photo { display: none; @@ -1595,6 +1593,40 @@ img.img_fullsize { } } +@media (max-height: 480px) { + .navbar { + min-height: 40px; + } + .tg_page_head .navbar > .container .navbar-brand { + padding: 4.5px 15px; + display: block; + /*overflow: hidden; + width: 49px; + height: 36px;*/ + } + .navbar-nav > li > a, + .tg_page_head .navbar-quick-nav a { + padding-top: 10px; + padding-bottom: 10px; + } + .navbar-toggle { + margin-top: 2px; + margin-bottom: 2px; + } + .im_page_footer { + display: none; + } + + .im_send_panel_wrap { + padding-bottom: 12px; + } + + .emoji-wysiwyg-editor { + min-height: 34px; + max-height: 150px; + } +} + @media (max-width: 640px) { .im_dialog_peer { white-space: normal; @@ -1726,4 +1758,81 @@ img.img_fullsize { .settings_user_phone, .settings_version { color: #999; +} + + +/* Contacts modal */ +.contacts_modal_window .modal-dialog { + max-width: 506px; +} +.contacts_modal_wrap .modal-body { + padding: 23px 25px 15px; +} + +.contacts_modal_search { + padding: 0 0 14px; + position: relative; +} +.contacts_modal_search_field { + font-size: 12px; + line-height: normal; + background: #F2F2F2 url(../img/icons/IconsetW.png?1) -6px -205px no-repeat; + background-size: 42px 430px; + border: 1px solid #F2F2F2; + border-radius: 3px; + padding: 6px 20px 6px 30px; + margin-bottom: 0; + margin: 0; +} +.is_1x .contacts_modal_search_field { + background-image: url(../img/icons/IconsetW_1x.png?2); +} +.contacts_modal_search_field:focus, +.contacts_modal_search_field:active { + background-color: #FFF; +} + +.contacts_modal_search_clear { + position: absolute; + right: 9px; + margin-top: -23px; + color: #999; + width: 13px; + height: 13px; + vertical-align: text-top; + background: url(../img/icons/IconsetW.png?1) -15px -192px no-repeat; + background-size: 42px 430px; + opacity: 0.6; +} +.is_1x .contacts_modal_search_clear { + background-image: url(../img/icons/IconsetW_1x.png?2); +} +.contacts_modal_search_clear:hover { + opacity: 1; +} + +.contacts_modal_contact_wrap { + padding: 8px 7px; + border-top: 1px solid #F0F0F0; +} +.contacts_modal_contact_wrap:first-child { + border-top: 0; +} +.contacts_modal_contact_name { + display: block; + color: #3C6E97; + font-weight: bold; + margin: 1px 0 2px; +} +.non_osx .contacts_modal_contact_name { + font-size: 12px; +} +.contacts_modal_contact_status { + color: #999; +} +.contacts_modal_contact_photo { + width: 40px; + height: 40px; + margin-right: 10px; + overflow: hidden; } \ No newline at end of file diff --git a/app/js/background.js b/app/js/background.js index 095e5b8f..8d90f26c 100644 --- a/app/js/background.js +++ b/app/js/background.js @@ -8,7 +8,7 @@ chrome.app.runtime.onLaunched.addListener(function(launchData) { chrome.app.window.create('../index.html', { bounds: { - width: 1100, + width: 900, height: 700 }, minWidth: 320, diff --git a/app/js/controllers.js b/app/js/controllers.js index 47e8f1d2..2efba23e 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -169,6 +169,15 @@ angular.module('myApp.controllers', []) }); } + $scope.openContacts = function () { + $modal.open({ + templateUrl: 'partials/contacts_modal.html?3', + controller: 'ContactsModalController', + scope: $rootScope.$new(), + windowClass: 'contacts_modal_window' + }); + } + updateCurDialog(); function updateCurDialog() { @@ -307,7 +316,8 @@ angular.module('myApp.controllers', []) hasMore = false, maxID = 0, startLimit = 20, - limit = 50; + limit = 50, + jump = 0; function applyDialogSelect (newPeer) { newPeer = newPeer || $scope.curDialog.peer || ''; @@ -356,7 +366,6 @@ angular.module('myApp.controllers', []) if (!hasMore || !offset) { return; } - // console.trace('load history'); AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID, limit).then(function (historyResult) { offset += limit; @@ -378,7 +387,11 @@ angular.module('myApp.controllers', []) offset = 0; maxID = 0; + var curJump = ++jump; + AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID, startLimit).then(function (historyResult) { + if (curJump != jump) return; + offset += startLimit; hasMore = offset < historyResult.count; maxID = historyResult.history[historyResult.history.length - 1]; @@ -781,5 +794,25 @@ angular.module('myApp.controllers', []) } }) + .controller('ContactsModalController', function ($scope, AppUsersManager) { + $scope.contacts = []; + $scope.search = []; + + $scope.$watch('search.query', function (newValue) { + AppUsersManager.getContacts(newValue).then(function (contactsList) { + $scope.contacts = []; + angular.forEach(contactsList, function(userID) { + var contact = { + userID: userID, + user: AppUsersManager.getUser(userID), + userPhoto: AppUsersManager.getUserPhoto(userID, 'User') + } + $scope.contacts.push(contact); + }); + }); + }) + + }) + diff --git a/app/js/directives.js b/app/js/directives.js index b0b46c6b..29dc79d9 100644 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -37,12 +37,10 @@ angular.module('myApp.directives', ['myApp.filters']) function link (scope, element, attrs) { - // console.log('init directive', element); - var dialogsWrap = $('.im_dialogs_wrap', element)[0], scrollableWrap = $('.im_dialogs_scrollable_wrap', element)[0], + headWrap = $('.tg_page_head')[0], footer = $('.im_page_footer')[0], - // dialogsSearch = $('im_dialogs_search', element)[0], moreNotified = false; onContentLoaded(function () { @@ -84,8 +82,11 @@ angular.module('myApp.directives', ['myApp.filters']) function updateSizes () { $(element).css({ - height: $($window).height() - footer.offsetHeight - 122 + height: $($window).height() - footer.offsetHeight - (headWrap ? headWrap.offsetHeight : 50) - 72 }); + if (!headWrap) { + headWrap = $('.tg_page_head')[0]; + } } $($window).on('resize', updateSizes); @@ -720,3 +721,13 @@ angular.module('myApp.directives', ['myApp.filters']) }); } }) + + .directive('myFocused', function(){ + return { + link: function(scope, element, attrs) { + setTimeout(function () { + element[0].focus(); + }, 100); + } + }; + }); diff --git a/app/js/services.js b/app/js/services.js index 14a291c0..272c004f 100644 --- a/app/js/services.js +++ b/app/js/services.js @@ -118,8 +118,58 @@ angular.module('myApp.services', []) }; }) -.service('AppUsersManager', function ($rootScope, $modal, MtpApiFileManager, MtpApiManager, RichTextProcessor) { - var users = {}; +.service('AppUsersManager', function ($rootScope, $modal, $modalStack, MtpApiFileManager, MtpApiManager, RichTextProcessor, SearchIndexManager) { + var users = {}, + contactsFillPromise, + contactsIndex = SearchIndexManager.createIndex(); + + function fillContacts () { + if (contactsFillPromise) { + return contactsFillPromise; + } + return contactsFillPromise = MtpApiManager.invokeApi('contacts.getContacts', { + hash: '' + }).then(function (result) { + var contactsList = [], + userID, searchText, i; + saveApiUsers(result.users); + + for (var i = 0; i < result.contacts.length; i++) { + userID = result.contacts[i].user_id; + contactsList.push(userID); + SearchIndexManager.indexObject(userID, getUserSearchText(userID), contactsIndex); + } + + return contactsList; + }); + } + + function getUserSearchText (id) { + var user = users[id]; + if (!user) { + return false; + } + + return (user.first_name || '') + ' ' + (user.last_name || '') + ' ' + (user.phone || ''); + } + + function getContacts (query) { + return fillContacts().then(function (contactsList) { + if (angular.isString(query) && query.length) { + var results = SearchIndexManager.search(query, contactsIndex), + filteredContactsList = []; + + for (var i = 0; i < contactsList.length; i++) { + if (results[contactsList[i]]) { + filteredContactsList.push(contactsList[i]) + } + } + contactsList = filteredContactsList; + } + + return contactsList; + }); + }; function saveApiUsers (apiUsers) { angular.forEach(apiUsers, saveApiUser); @@ -137,6 +187,9 @@ angular.module('myApp.services', []) apiUser.rFirstName = RichTextProcessor.wrapRichText(apiUser.last_name, {noLinks: true, noLinebreaks: true}) || 'DELETED'; apiUser.rFullName = RichTextProcessor.wrapRichText(apiUser.last_name, {noLinks: true, noLinebreaks: true}) || 'DELETED'; } + apiUser.sortName = $.trim((apiUser.last_name || '') + ' ' + apiUser.first_name); + apiUser.sortStatus = apiUser.status && (apiUser.status.expires || apiUser.status.was_online) || 0; + if (users[apiUser.id] === undefined) { users[apiUser.id] = apiUser; @@ -229,9 +282,11 @@ angular.module('myApp.services', []) // console.log('on apiUpdate', update); switch (update._) { case 'updateUserStatus': - var userID = update.user_id; - if (users[userID]) { - users[userID].status = update.status; + var userID = update.user_id, + user = users[userID]; + if (user) { + user.status = update.status; + user.sortStatus = update.status && (update.status.expires || update.status.was_online) || 0; $rootScope.$broadcast('user_update', userID); } break; @@ -248,11 +303,13 @@ angular.module('myApp.services', []) return { + getContacts: getContacts, saveApiUsers: saveApiUsers, saveApiUser: saveApiUser, getUser: getUser, getUserPhoto: getUserPhoto, getUserString: getUserString, + getUserSearchText: getUserSearchText, hasUser: hasUser, wrapForFull: wrapForFull, openUser: openUser @@ -379,8 +436,7 @@ angular.module('myApp.services', []) getPeerSearchText: function (peerID) { var text; if (peerID > 0) { - var user = AppUsersManager.getUser(peerID); - text = (user.first_name || '') + ' ' + (user.last_name || '') + ' ' + (user.phone || ''); + text = AppUsersManager.getUserSearchText(peerID); } else if (peerID < 0) { var chat = AppChatsManager.getChat(-peerID); text = chat.title || ''; @@ -463,6 +519,7 @@ angular.module('myApp.services', []) } function search (query, searchIndex) { + console.time('search'); var shortIndexes = searchIndex.shortIndexes, fullTexts = searchIndex.fullTexts; @@ -499,11 +556,12 @@ angular.module('myApp.services', []) } } + console.timeEnd('search'); return newFoundObjs; } }) -.service('AppMessagesManager', function ($q, $rootScope, $filter, $sanitize, $location, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, AppAudioManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, SearchIndexManager) { +.service('AppMessagesManager', function ($q, $rootScope, $location, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, AppAudioManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, SearchIndexManager) { var messagesStorage = {}; var messagesForHistory = {}; @@ -706,7 +764,7 @@ angular.module('myApp.services', []) } function readHistory (inputPeer) { - // console.log('start read'); + // console.trace('start read'); var peerID = AppPeersManager.getPeerID(inputPeer), historyStorage = historiesStorage[peerID], foundDialog = getDialogByPeerID(peerID); @@ -718,7 +776,8 @@ angular.module('myApp.services', []) return false; } - var wasUnread = false; + var messageID, + message; // console.log(historyStorage); for (i = 0; i < historyStorage.history.length; i++) { messageID = historyStorage.history[i]; @@ -727,11 +786,10 @@ angular.module('myApp.services', []) if (message && !message.out) { if (message.unread) { // console.log('unread'); - wasUnread = true; - } else if (!wasUnread) { - // console.log('bad2'); - return false; + break; } + // console.log('bad2', message); + return false; } } @@ -743,6 +801,7 @@ angular.module('myApp.services', []) return processAffectedHistory(inputPeer, affectedHistory, 'messages.readHistory'); }).then(function () { if (foundDialog[0]) { + // console.log('done read history', peerID); foundDialog[0].unread_count = 0; $rootScope.$broadcast('dialog_unread', {peerID: peerID, count: 0}); } @@ -768,7 +827,7 @@ angular.module('myApp.services', []) } function flushHistory (inputPeer) { - // console.log('start read'); + // console.log('start flush'); var peerID = AppPeersManager.getPeerID(inputPeer), historyStorage = historiesStorage[peerID]; @@ -1242,7 +1301,7 @@ angular.module('myApp.services', []) } $rootScope.$on('apiUpdate', function (e, update) { - console.log('on apiUpdate', update); + // console.log('on apiUpdate', update); switch (update._) { case 'updateMessageID': pendingByMessageID[update.id] = update.random_id; @@ -1295,10 +1354,13 @@ angular.module('myApp.services', []) dialog = {peerID: peerID, unread_count: 0, top_message: false} } if (!message.out && message.unread) { + // console.log('inc unread count', dialog.unread_count); dialog.unread_count++; } dialog.top_message = message.id; + // console.log('new message', message, peerID, historyStorage, foundDialog, dialog); + SearchIndexManager.indexObject(peerID, AppPeersManager.getPeerSearchText(peerID), dialogsIndex); dialogsStorage.dialogs.unshift(dialog); @@ -1321,8 +1383,8 @@ angular.module('myApp.services', []) for (i = 0; i < update.messages.length; i++) { messageID = update.messages[i]; message = messagesStorage[messageID]; - // console.log('read', messageID, message); - if (message) { + // console.log('read', messageID, message.unread, message); + if (message && message.unread) { message.unread = false; if (messagesForHistory[messageID]) { messagesForHistory[messageID].unread = false; @@ -2015,8 +2077,8 @@ angular.module('myApp.services', []) } if (seqStart != curState.seq + 1) { - // console.log('seq hole', seqStart, curState.seq); - if (seqStart != curState.seq) { + if (seqStart > curState.seq) { + console.warn('Seq hole', seqStart, curState.seq); getDifference(); } return false; diff --git a/app/partials/chat_modal.html b/app/partials/chat_modal.html index 6e34f90c..b3c778d6 100644 --- a/app/partials/chat_modal.html +++ b/app/partials/chat_modal.html @@ -48,14 +48,14 @@
Members
-
+
- +
diff --git a/app/partials/contacts_modal.html b/app/partials/contacts_modal.html new file mode 100644 index 00000000..2de41f67 --- /dev/null +++ b/app/partials/contacts_modal.html @@ -0,0 +1,36 @@ +
+ + + + + +
\ No newline at end of file diff --git a/app/partials/head.html b/app/partials/head.html index c3b27b2a..12d8bcf4 100644 --- a/app/partials/head.html +++ b/app/partials/head.html @@ -21,8 +21,9 @@
diff --git a/app/partials/im.html b/app/partials/im.html index 56ff934a..bb3c29cf 100644 --- a/app/partials/im.html +++ b/app/partials/im.html @@ -13,7 +13,7 @@
diff --git a/app/vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.10.0.js b/app/vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.10.0.js index d49a0f9b..bebf9ccd 100644 --- a/app/vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.10.0.js +++ b/app/vendor/ui-bootstrap/ui-bootstrap-custom-tpls-0.10.0.js @@ -1068,13 +1068,13 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) { $templateCache.put("template/modal/backdrop.html", - "
"); + "
"); }]); angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) { $templateCache.put("template/modal/window.html", "
\n" + - "
\n" + + "
\n" + "
\n" + "
\n" + "
\n" +