diff --git a/css/app.css b/css/app.css index 7defae5b..5f9ea882 100644 --- a/css/app.css +++ b/css/app.css @@ -256,7 +256,7 @@ input[type="number"]::-webkit-inner-spin-button { vertical-align: text-top; background: url(../img/icons/IconsetW.png?1) -15px -419px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; opacity: 0.6; } .is_1x .icon-back { @@ -511,7 +511,7 @@ input[type="number"]::-webkit-inner-spin-button { font-size: 12px; line-height: normal; background: #F2F2F2 url(../img/icons/IconsetW.png?1) -6px -205px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; border: 1px solid #F2F2F2; border-radius: 3px; padding: 6px 20px 6px 30px; @@ -535,7 +535,7 @@ input[type="number"]::-webkit-inner-spin-button { height: 13px; vertical-align: text-top; background: url(../img/icons/IconsetW.png?1) -15px -192px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; opacity: 0.6; } .is_1x .im_dialogs_search_clear { @@ -788,7 +788,7 @@ a.im_dialog:hover .im_dialog_date { margin-left: 6px; background: url(../img/icons/IconsetW.png?1) -17px -444px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; } .is_1x .icon-caret { background-image: url(../img/icons/IconsetW_1x.png?2); @@ -896,6 +896,13 @@ a.im_dialog:hover .im_dialog_date { opacity: 0; }*/ +.im_message_unread_split { + background: #E9EBED; + text-align: center; + padding: 4px 10px; + margin: 10px 0; +} + .im_message_author { @@ -987,7 +994,7 @@ div.im_message_video_thumb { height: 19px; background: url(../img/icons/IconsetW.png?1) -14px -389px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; } .is_1x .icon-geo-point { background-image: url(../img/icons/IconsetW_1x.png?2); @@ -1015,7 +1022,7 @@ div.im_message_video_thumb { vertical-align: text-top; background: #F2F2F2 url(../img/icons/IconsetW.png?1) -2px -229px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; border-radius: 3px; margin-right: 10px; } @@ -1032,6 +1039,7 @@ div.im_message_video_thumb { .im_history_selectable .im_message_outer_wrap:hover .icon-photo, .im_history_selectable .im_message_outer_wrap:hover .icon-video { background-color: #dae6f0; + background-position: -2px -542px; } .im_message_document_info { @@ -1076,7 +1084,7 @@ div.im_message_video_thumb { vertical-align: text-top; background: #F2F2F2 url(../img/icons/IconsetW.png?1) -2px -277px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; border-radius: 3px; margin-right: 10px; } @@ -1335,7 +1343,7 @@ textarea.im_message_field { height: 23px; vertical-align: text-top; background: url(../img/icons/IconsetW.png?1) -12px -68px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; opacity: 0.8; } .is_1x .icon-paperclip { @@ -1363,7 +1371,7 @@ textarea.im_message_field { height: 23px; vertical-align: text-top; background: url(../img/icons/IconsetW.png?1) -10px -4px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; opacity: 0.8; } .is_1x .icon-emoji { @@ -1413,7 +1421,7 @@ textarea.im_message_field { height: 21px; vertical-align: text-top; background: url(../img/icons/IconsetW.png?1) -9px -132px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; opacity: 0.8; } .is_1x .icon-camera { @@ -1833,7 +1841,7 @@ img.img_fullsize { .emoji-menu-tail { display: none; background: url(../img/icons/IconsetW.png?1) -14px -268px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; width: 14px; height: 7px; margin: 0 83px; @@ -1984,7 +1992,7 @@ img.img_fullsize { font-size: 12px; line-height: normal; background: #F2F2F2 url(../img/icons/IconsetW.png?1) -6px -205px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; border: 1px solid #F2F2F2; border-radius: 3px; padding: 6px 20px 6px 30px; @@ -2008,7 +2016,7 @@ img.img_fullsize { height: 13px; vertical-align: text-top; background: url(../img/icons/IconsetW.png?1) -15px -192px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; opacity: 0.6; } .is_1x .contacts_modal_search_clear { @@ -2085,7 +2093,7 @@ a.contacts_modal_contact:hover .contacts_modal_contact_status { width: 17px; height: 15px; background: url(../img/icons/IconsetW.png?1) -13px -366px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; opacity: 0.5; } .is_1x .icon-contact-tick { @@ -2183,7 +2191,7 @@ a.contacts_modal_contact:hover .contacts_modal_contact_status { height: 15px; background: url(../img/icons/IconsetW.png?1) -15px -319px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; } .is_1x .icon-delete { background-image: url(../img/icons/IconsetW_1x.png?2); @@ -2230,7 +2238,7 @@ a.contacts_modal_contact:hover .contacts_modal_contact_status { height: 26px; margin: 13px 0 0 40px; background: url(../img/icons/IconsetW.png?1) -9px -516px no-repeat; - background-size: 42px 560px; + background-size: 42px 620px; } .is_1x .icon-select-tick { background-image: url(../img/icons/IconsetW_1x.png?2); diff --git a/img/icons/IconsetW.png b/img/icons/IconsetW.png index d162ef29..ee6a46c4 100644 Binary files a/img/icons/IconsetW.png and b/img/icons/IconsetW.png differ diff --git a/img/icons/IconsetW_1x.png b/img/icons/IconsetW_1x.png index 1e049450..efa92f21 100644 Binary files a/img/icons/IconsetW_1x.png and b/img/icons/IconsetW_1x.png differ diff --git a/js/controllers.js b/js/controllers.js index 30cdbd95..dd7c8a49 100644 --- a/js/controllers.js +++ b/js/controllers.js @@ -447,12 +447,12 @@ angular.module('myApp.controllers', []) inputMediaFilter = $scope.mediaType && {_: inputMediaFilters[$scope.mediaType]}, getMessagesPromise = inputMediaFilter ? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID, startLimit) - : AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID, startLimit); + : AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID); getMessagesPromise.then(function (historyResult) { if (curJump != jump) return; - offset += startLimit; + offset += historyResult.history.length; hasMore = offset < historyResult.count; maxID = historyResult.history[historyResult.history.length - 1]; @@ -462,6 +462,15 @@ angular.module('myApp.controllers', []) }); $scope.history.reverse(); + if (historyResult.unreadLimit) { + $scope.historyUnread = { + beforeID: historyResult.history[historyResult.unreadLimit - 1], + count: historyResult.unreadLimit + }; + } else { + $scope.historyUnread = {}; + } + safeReplaceObject($scope.state, {loaded: true}); $scope.$broadcast('ui_history_change'); @@ -589,6 +598,8 @@ angular.module('myApp.controllers', []) $scope.history.push(AppMessagesManager.wrapForHistory(addedMessage.messageID)); $scope.typing = {}; $scope.$broadcast('ui_history_append', {my: addedMessage.my}); + $scope.historyUnread = {}; + offset++; // console.log('append check', $rootScope.idle.isIDLE, addedMessage.peerID, $scope.curDialog.peerID); @@ -728,7 +739,7 @@ angular.module('myApp.controllers', []) // console.trace('ctrl text changed', newVal); AppMessagesManager.readHistory($scope.curDialog.inputPeer); - if (newVal.length) { + if (newVal && newVal.length) { var backupDraftObj = {}; backupDraftObj['draft' + $scope.curDialog.peerID] = newVal; AppConfigManager.set(backupDraftObj); diff --git a/js/directives.js b/js/directives.js index 29bbd22d..e49ad249 100644 --- a/js/directives.js +++ b/js/directives.js @@ -215,8 +215,16 @@ angular.module('myApp.directives', ['myApp.filters']) onContentLoaded(function () { $(scrollableWrap).removeClass('im_history_to_bottom'); $(scrollable).css({bottom: ''}); - updateSizes(); - scrollableWrap.scrollTop = scrollableWrap.scrollHeight; + updateSizes(true); + + var unreadSplit = $('.im_message_unread_split', scrollableWrap); + if (unreadSplit[0]) { + scrollableWrap.scrollTop = unreadSplit[0].offsetTop; + atBottom = false; + } else { + scrollableWrap.scrollTop = scrollableWrap.scrollHeight; + } + updateScroller(); moreNotified = false; }); @@ -429,6 +437,7 @@ angular.module('myApp.directives', ['myApp.filters']) }; $('body').on('dragenter dragleave dragover drop', onDragDropEvent); + $(document).on('paste', onPasteEvent); scope.$on('ui_peer_change', focusField); scope.$on('ui_history_focus', focusField); @@ -440,6 +449,7 @@ angular.module('myApp.directives', ['myApp.filters']) scope.$on('$destroy', function cleanup() { $('body').off('dragenter dragleave dragover drop', onDragDropEvent); + $(document).off('paste', onPasteEvent); }); focusField(); @@ -450,6 +460,26 @@ angular.module('myApp.directives', ['myApp.filters']) }); } + function onPasteEvent (e) { + var cData = (e.originalEvent || e).clipboardData, + items = cData && cData.items || [], + files = [], + i; + + for (i = 0; i < items.length; i++) { + if (items[i].kind == 'file') { + files.push(items[i].getAsFile()); + } + } + + if (files.length && safeConfirm('Are you sure to send file(s) from clipboard?')) { + scope.$apply(function () { + scope.draftMessage.files = files; + scope.draftMessage.isMedia = true; + }); + } + } + function onDragDropEvent(e) { var dragStateChanged = false; if (!dragStarted || dragStarted == 1) { @@ -696,7 +726,8 @@ angular.module('myApp.directives', ['myApp.filters']) MtpApiFileManager.downloadFile(scope.video.dc_id, inputLocation, scope.video.size, null, {mime: 'video/mp4'}).then(function (url) { scope.progress.enabled = false; // scope.progress = {enabled: true, percent: 50}; - scope.player.quicktime = hasQt; + scope.player.hasQuicktime = hasQt; + scope.player.quicktime = false; scope.player.src = $sce.trustAsResourceUrl(url); }, function (e) { console.log('Download video failed', e, scope.video); diff --git a/js/services.js b/js/services.js index 928563b6..5a84dbf1 100644 --- a/js/services.js +++ b/js/services.js @@ -134,6 +134,7 @@ angular.module('myApp.services', []) var users = {}, cachedPhotoLocations = {}, contactsFillPromise, + contactsList, contactsIndex = SearchIndexManager.createIndex(); function fillContacts () { @@ -143,8 +144,8 @@ angular.module('myApp.services', []) return contactsFillPromise = MtpApiManager.invokeApi('contacts.getContacts', { hash: '' }).then(function (result) { - var contactsList = [], - userID, searchText, i; + var userID, searchText, i; + contactsList = []; saveApiUsers(result.users); for (var i = 0; i < result.contacts.length; i++) { @@ -323,6 +324,24 @@ angular.module('myApp.services', []) $rootScope.$broadcast('user_update', userID); } break; + + case 'updateContactLink': + if (angular.isArray(contactsList)) { + var userID = update.user_id, + curPos = curIsContact = contactsList.indexOf(userID), + curIsContact = curPos != -1, + newIsContact = update.my_link._ == 'contacts.myLinkContact'; + + if (newIsContact != curIsContact) { + if (newIsContact) { + contactsList.push(userID); + SearchIndexManager.indexObject(userID, getUserSearchText(userID), contactsIndex); + } else { + contactsList.splice(curPos, 1); + } + } + } + break; } }); @@ -729,6 +748,46 @@ angular.module('myApp.services', []) return deferred.promise; } + function fillHistoryStorage (inputPeer, maxID, fullLimit, historyStorage) { + return MtpApiManager.invokeApi('messages.getHistory', { + peer: inputPeer, + offset: 0, + limit: fullLimit, + max_id: maxID || 0 + }).then(function (historyResult) { + AppUsersManager.saveApiUsers(historyResult.users); + AppChatsManager.saveApiChats(historyResult.chats); + saveMessages(historyResult.messages); + + historyStorage.count = historyResult._ == 'messages.messagesSlice' + ? historyResult.count + : historyResult.messages.length; + + var offset = 0; + if (maxID > 0) { + for (offset = 0; offset < historyStorage.history.length; offset++) { + if (maxID > historyStorage.history[offset]) { + break; + } + } + } + + historyStorage.history.splice(offset, historyStorage.history.length - offset); + angular.forEach(historyResult.messages, function (message) { + historyStorage.history.push(message.id); + }); + + fullLimit -= historyResult.messages.length; + + if (fullLimit > 0 && historyStorage.history.length < historyStorage.count) { + maxID = historyStorage.history[historyStorage.history.length - 1]; + return fillHistoryStorage(inputPeer, maxID, fullLimit, historyStorage); + } + + return true; + }); + }; + function getHistory (inputPeer, maxID, limit) { var peerID = AppPeersManager.getPeerID(inputPeer), @@ -743,6 +802,17 @@ angular.module('myApp.services', []) resultPending = historyStorage.pending.slice(); } + var unreadLimit = false; + if (!limit && !maxID) { + var foundDialog = getDialogByPeerID(peerID); + if (foundDialog && foundDialog[0] && foundDialog[0].unread_count > 0) { + unreadLimit = foundDialog[0].unread_count; + limit = Math.max(20, unreadLimit + 2); + } + } + if (!limit) { + limit = 20; + } if (maxID > 0) { for (offset = 0; offset < historyStorage.history.length; offset++) { @@ -751,7 +821,6 @@ angular.module('myApp.services', []) } } } - // console.log('history storage', angular.copy(historyStorage.history), maxID, offset); if (historyStorage.count !== null && ( historyStorage.history.length >= offset + limit || @@ -759,26 +828,12 @@ angular.module('myApp.services', []) )) { return $q.when({ count: historyStorage.count, - history: resultPending.concat(historyStorage.history.slice(offset, offset + limit)) + history: resultPending.concat(historyStorage.history.slice(offset, offset + limit)), + unreadLimit: unreadLimit }); } - var deferred = $q.defer(); - - MtpApiManager.invokeApi('messages.getHistory', { - peer: inputPeer, - offset: offset, - limit: limit, - max_id: maxID || 0 - }).then(function (historyResult) { - AppUsersManager.saveApiUsers(historyResult.users); - AppChatsManager.saveApiChats(historyResult.chats); - saveMessages(historyResult.messages); - - historyStorage.count = historyResult._ == 'messages.messagesSlice' - ? historyResult.count - : historyResult.messages.length; - + return fillHistoryStorage(inputPeer, maxID, limit, historyStorage).then(function () { offset = 0; if (maxID > 0) { for (offset = 0; offset < historyStorage.history.length; offset++) { @@ -788,23 +843,12 @@ angular.module('myApp.services', []) } } - // console.log('history storage after', angular.copy(historyStorage.history), historyResult.messages, maxID, offset); - - historyStorage.history.splice(offset, historyStorage.history.length - offset); - angular.forEach(historyResult.messages, function (message) { - historyStorage.history.push(message.id); - }); - // console.log('history storage final', angular.copy(historyStorage.history), historyResult.messages, maxID, offset); - - deferred.resolve({ + return { count: historyStorage.count, - history: resultPending.concat(historyStorage.history.slice(offset, offset + limit)) - }); - }, function (error) { - deferred.reject(error); + history: resultPending.concat(historyStorage.history.slice(offset, offset + limit)), + unreadLimit: unreadLimit + }; }); - - return deferred.promise; } function getSearch (inputPeer, query, inputFilter, maxID, limit) { @@ -1072,18 +1116,27 @@ angular.module('myApp.services', []) randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString(), historyStorage = historiesStorage[peerID], inputPeer = AppPeersManager.getInputPeerByID(peerID), - attachType; + attachType, fileName, fileName; if (!options.isMedia) { attachType = 'doc'; + fileName = 'doc.' + file.type.split('/')[1]; } else if (['image/jpeg', 'image/gif', 'image/png', 'image/bmp'].indexOf(file.type) >= 0) { attachType = 'photo'; + fileName = 'photo.' + file.type.split('/')[1]; } else if (file.type.substr(0, 6) == 'video/') { attachType = 'video'; + fileName = 'video.mp4'; } else if (file.type == 'audio/mpeg' || file.type == 'audio/mp3') { attachType = 'audio'; + fileName = 'audio.mp3'; } else { attachType = 'doc'; + fileName = 'doc.' + file.type.split('/')[1]; + } + + if (!file.name) { + file.name = fileName; } if (historyStorage === undefined) { @@ -1113,6 +1166,21 @@ angular.module('myApp.services', []) pending: true }; + var toggleError = function (on) { + var historyMessage = messagesForHistory[messageID]; + if (on) { + message.error = true; + if (historyMessage) { + historyMessage.error = true; + } + } else { + delete message.error; + if (historyMessage) { + delete historyMessage.error; + } + } + } + message.send = function () { MtpApiFileManager.uploadFile(file).then(function (inputFile) { var inputMedia; diff --git a/partials/message.html b/partials/message.html index 99100d3e..fb1929fb 100644 --- a/partials/message.html +++ b/partials/message.html @@ -1,3 +1,9 @@ +
+