From 8d1f2706a509dbbb2e41664991c27fe41c233e2b Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Thu, 9 Jan 2014 16:13:12 +0400 Subject: [PATCH] Added desktop notififactions, added download/upload progress --- app/css/app.css | 45 ++++++-- app/index.html | 10 +- app/js/controllers.js | 62 ++++++++--- app/js/directives.js | 19 ++-- app/js/lib/mtproto.js | 6 +- app/js/services.js | 213 +++++++++++++++++++++++++++++++++++++- app/partials/message.html | 14 +-- 7 files changed, 315 insertions(+), 54 deletions(-) diff --git a/app/css/app.css b/app/css/app.css index 5675d5ef..62a39e8d 100644 --- a/app/css/app.css +++ b/app/css/app.css @@ -715,6 +715,8 @@ div.im_message_video_thumb { border-radius: 3px; display: inline-block; line-height: 0; + + width: 300px; } .icon-document { display: inline-block; @@ -729,8 +731,26 @@ div.im_message_video_thumb { .im_message_document .icon-group { background-image: url(../img/icons/DialogListGroupChatIcon_Highlighted@2x.png); } -.im_message_document_name { + +.im_message_document_size { color: #999; + float: right; + + vertical-align: text-top; + display: inline-block; + line-height: 20px; + padding: 9px 3px 9px 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.im_message_document:hover .im_message_document_size { + color: #698192; +} + +.im_message_document_name { + color: #000; + font-weight: bold; vertical-align: text-top; display: inline-block; line-height: 20px; @@ -749,17 +769,11 @@ div.im_message_video_thumb { background: #EBF0F5 url(../img/icons/DocBlue_2x.png) 8px 10px no-repeat; background-size: 20px 20px; } -.im_message_document:hover .im_message_document_name { - color: #698192; -} -.im_message_document_name strong { - color: #000; - padding-right: 3px; -} .im_message_upload_progress_wrap, .im_message_download_progress_wrap { margin-top: 5px; + width: 300px; } .tg_up_progress, @@ -767,21 +781,30 @@ div.im_message_video_thumb { height: 5px; margin: 0; padding: 0; - background: rgba(0,0,0, 0.1); + /*background: rgba(0,0,0, 0.1);*/ + background: #E9EBED; border: 0; border-radius: 4px; + -webkit-box-shadow: none; + box-shadow: none; } .tg_up_progress .progress-bar, .tg_down_progress .progress-bar { height: 5px; line-height: 5px; - background: #43A4DB; + /*background: #43A4DB;*/ + background: #B3BFC7; border-radius: 3px; overflow: hidden; + -webkit-box-shadow: none; + box-shadow: none; } .tg_down_progress .progress-bar { - background: #6DBF69; + background: #A1D2ED; + /*background: #6DBF69;*/ } +/*.tg_up_progress .progress-bar { +}*/ diff --git a/app/index.html b/app/index.html index 1a806b4c..07eacfac 100644 --- a/app/index.html +++ b/app/index.html @@ -7,7 +7,7 @@ - + @@ -36,10 +36,10 @@ - - - - + + + + diff --git a/app/js/controllers.js b/app/js/controllers.js index b973eb71..38595c1c 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -207,12 +207,14 @@ angular.module('myApp.controllers', []) }) - .controller('AppImHistoryController', function ($scope, $location, $timeout, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager) { + .controller('AppImHistoryController', function ($scope, $location, $timeout, $rootScope, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager, IdleManager) { $scope.$watch('curDialog.peer', applyDialogSelect); ApiUpdatesManager.attach(); + IdleManager.start(); + $scope.history = []; $scope.typing = {}; @@ -227,12 +229,39 @@ angular.module('myApp.controllers', []) $scope.curDialog.inputPeer = AppPeersManager.getInputPeer(newPeer); if (peerID) { + updateHistoryPeer(true); loadHistory(peerID); } else { showEmptyHistory(); } } + function updateHistoryPeer(preload) { + var peerData = AppPeersManager.getPeer(peerID); + dLog('update', preload, peerData); + if (!peerData || peerData.deleted) { + return false; + } + + $scope.history = []; + + $scope.historyPeer = { + id: peerID, + data: peerData, + photo: AppPeersManager.getPeerPhoto(peerID, 'User', 'Group') + }; + + MtpApiManager.getUserID().then(function (id) { + $scope.ownPhoto = AppUsersManager.getUserPhoto(id, 'User'); + }); + + if (preload) { + $scope.typing = {}; + $scope.state = {loaded: true}; + $scope.$broadcast('ui_peer_change'); + } + } + function showMoreHistory () { if (!hasMore || !offset) { return; @@ -264,24 +293,12 @@ angular.module('myApp.controllers', []) hasMore = offset < historyResult.count; maxID = historyResult.history[historyResult.history.length - 1]; - $scope.history = []; + updateHistoryPeer(); angular.forEach(historyResult.history, function (id) { $scope.history.push(AppMessagesManager.wrapForHistory(id)); }); $scope.history.reverse(); - $scope.historyPeer = { - id: peerID, - data: AppPeersManager.getPeer(peerID), - photo: AppPeersManager.getPeerPhoto(peerID, 'User', 'Group') - }; - - $scope.typing = {}; - - MtpApiManager.getUserID().then(function (id) { - $scope.ownPhoto = AppUsersManager.getUserPhoto(id, 'User'); - }); - $scope.state = {loaded: true}; $scope.$broadcast('ui_history_change'); @@ -305,12 +322,19 @@ angular.module('myApp.controllers', []) $scope.$on('history_append', function (e, addedMessage) { if (addedMessage.peerID == $scope.curDialog.peerID) { - dLog('append', addedMessage); + // dLog('append', addedMessage); // console.trace(); $scope.history.push(AppMessagesManager.wrapForHistory(addedMessage.messageID)); $scope.typing = {}; $scope.$broadcast('ui_history_append'); - offset++ + offset++; + + // dLog('append check', $rootScope.idle.isIDLE, addedMessage.peerID, $scope.curDialog.peerID); + if (!$rootScope.idle.isIDLE) { + $timeout(function () { + AppMessagesManager.readHistory($scope.curDialog.inputPeer); + }); + } } }); @@ -347,6 +371,12 @@ angular.module('myApp.controllers', []) showMoreHistory(); }); + $rootScope.$watch('idle.isIDLE', function (newVal) { + if (!newVal && $scope.curDialog && $scope.curDialog.peerID) { + AppMessagesManager.readHistory($scope.curDialog.inputPeer); + } + }); + }) .controller('AppImPanelController', function($scope) { diff --git a/app/js/directives.js b/app/js/directives.js index 7aad2f64..d99ec109 100644 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -25,7 +25,7 @@ angular.module('myApp.directives', ['myApp.filters']) restrict: 'AE', scope: true, translude: false, - templateUrl: 'partials/message.html' + templateUrl: 'partials/message.html?1' }; }) @@ -114,12 +114,15 @@ angular.module('myApp.directives', ['myApp.filters']) } scope.$on('ui_history_append', function () { - var st = scrollableWrap.scrollTop; + // var st = scrollableWrap.scrollTop; $(scrollableWrap).addClass('im_history_to_bottom'); + $(scrollable).css({bottom: 0}); + if (atBottom) { onContentLoaded(function () { $(scrollableWrap).removeClass('im_history_to_bottom'); - updateSizes(); + $(scrollable).css({bottom: ''}); + // updateSizes(true); $(historyWrap).nanoScroller({scrollBottom: 0}); // scrollableWrap.scrollTop = st; // $(scrollableWrap).animate({ @@ -175,10 +178,12 @@ angular.module('myApp.directives', ['myApp.filters']) } }); - function updateSizes () { + function updateSizes (heightOnly) { $(historyWrap).css({ height: $($window).height() - panelWrap.offsetHeight - sendFormWrap.offsetHeight - 90 }); + + if (heightOnly) return; if (atBottom) { onContentLoaded(function () { $(historyWrap).nanoScroller({scroll: 'bottom'}); @@ -243,7 +248,7 @@ angular.module('myApp.directives', ['myApp.filters']) if (submit) { $(element).trigger('submit'); - dLog('after submit'); + // dLog('after submit'); return cancelEvent(e); } }); @@ -261,9 +266,7 @@ angular.module('myApp.directives', ['myApp.filters']) $('body').on('dragenter dragleave dragover drop', onDragDropEvent); - scope.$on('ui_history_change', focusField); - scope.$on('ui_message_send', focusField); - + scope.$on('ui_peer_change ui_history_change ui_message_send', focusField); scope.$on('$destroy', function cleanup() { $('body').off('dragenter dragleave dragover drop', onDragDropEvent); }); diff --git a/app/js/lib/mtproto.js b/app/js/lib/mtproto.js index 43e1b44d..0b029e9b 100644 --- a/app/js/lib/mtproto.js +++ b/app/js/lib/mtproto.js @@ -1566,10 +1566,10 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato serializer.storeInt(962726977, 'InokeWithLayer10'); serializer.storeInt(0x69796de9, 'initConnection'); serializer.storeInt(777, 'api_id'); - serializer.storeString(navigator.userAgent, 'device_model'); - serializer.storeString(navigator.platform, 'system_version'); + serializer.storeString(navigator.userAgent || 'Unknown UserAgent', 'device_model'); + serializer.storeString(navigator.platform || 'Unknown Platform', 'system_version'); serializer.storeString('0.1', 'app_version'); - serializer.storeString(navigator.language, 'lang_code'); + serializer.storeString(navigator.language || 'en', 'lang_code'); } serializer.storeMethod(method, params); diff --git a/app/js/services.js b/app/js/services.js index 7c27184f..709f7224 100644 --- a/app/js/services.js +++ b/app/js/services.js @@ -405,7 +405,7 @@ angular.module('myApp.services', []) } }) -.service('AppMessagesManager', function ($q, $rootScope, $filter, $sanitize, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, MtpApiManager, MtpApiFileManager, RichTextProcessor) { +.service('AppMessagesManager', function ($q, $rootScope, $filter, $sanitize, $location, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager) { var messagesStorage = {}; var messagesForHistory = {}; @@ -415,6 +415,8 @@ angular.module('myApp.services', []) var pendingByMessageID = {}; var tempID = -1; + NotificationsManager.start(); + function getDialogs (offset, limit) { if (dialogsStorage.count !== null && dialogsStorage.dialogs.length >= offset + limit) { return $q.when({ @@ -494,7 +496,7 @@ angular.module('myApp.services', []) peer: inputPeer, offset: offset, limit: limit, - max_id: 0 + max_id: maxID || 0 }).then(function (historyResult) { AppUsersManager.saveApiUsers(historyResult.users); AppChatsManager.saveApiChats(historyResult.chats); @@ -519,6 +521,7 @@ angular.module('myApp.services', []) angular.forEach(historyResult.messages, function (message) { historyStorage.history.push(message.id); }); + // dLog('history storage final', angular.copy(historyStorage.history), historyResult.messages, maxID, offset); deferred.resolve({ count: historyStorage.count, @@ -557,7 +560,7 @@ angular.module('myApp.services', []) if (!historyStorage || !historyStorage.history.length || foundDialog[0] && !foundDialog[0].unread_count) { - // dLog('bad1'); + // dLog('bad1', historyStorage, foundDialog[0]); return false; } @@ -866,8 +869,8 @@ angular.module('myApp.services', []) return message; } - function wrapForHistory (msgID, force) { - if (!force && messagesForHistory[msgID] !== undefined) { + function wrapForHistory (msgID) { + if (messagesForHistory[msgID] !== undefined) { return messagesForHistory[msgID]; } @@ -983,6 +986,58 @@ angular.module('myApp.services', []) dialog.top_message = message.id; dialogsStorage.dialogs.unshift(dialog); $rootScope.$broadcast('dialogs_update', dialog); + + + if ($rootScope.idle.isIDLE && !message.out && message.unread) { + var fromUser = AppUsersManager.getUser(message.from_id); + var fromPhoto = AppUsersManager.getUserPhoto(message.from_id, 'User'); + var peerString; + var notification = {}, + notificationPhoto; + + if (peerID > 0) { + notification.title = (fromUser.first_name || '') + + (fromUser.first_name && fromUser.last_name ? ' ' : '') + + (fromUser.last_name || ''); + + notification.message = message.message; + + notificationPhoto = fromPhoto; + + peerString = AppUsersManager.getUserString(peerID); + + } else { + notification.title = fromUser.first_name || fromUser.last_name || 'Somebody' + + ' @ ' + + AppChatsManager.getChat(-peerID).title || 'Unknown chat'; + + notification.message = message.message; + + notificationPhoto = AppChatsManager.getChatPhoto(-peerID); + + peerString = AppChatsManager.getChatString(-peerID); + } + + notification.onclick = function () { + $location.url('/im?p=' + peerString); + }; + + notification.image = notificationPhoto.placeholder; + + if (notificationPhoto.location) { + MtpApiFileManager.downloadSmallFile(notificationPhoto.location, notificationPhoto.size).then(function (url) { + notification.image = url; + + if (message.unread) { + // dLog(111, notification); + NotificationsManager.notify(notification); + } + }); + } else { + // dLog(222, notification); + NotificationsManager.notify(notification); + } + } break; case 'updateReadMessages': @@ -1685,3 +1740,151 @@ angular.module('myApp.services', []) } }) + + +.service('IdleManager', function ($rootScope, $window, $timeout) { + + $rootScope.idle = {isIDLE: false}; + + var toPromise; + + return { + start: start + }; + + function start () { + $($window).on('blur focus keydown mousedown touchstart', onEvent); + } + + function onEvent (e) { + // dLog('event', e.type); + if (e.type == 'mousemove') { + $($window).off('mousemove', onEvent); + } + var isIDLE = e.type == 'blur' || e.type == 'timeout' ? true : false; + + $timeout.cancel(toPromise); + if (!isIDLE) { + // dLog('update timeout'); + toPromise = $timeout(function () { + onEvent({type: 'timeout'}); + }, 30000); + } + + if ($rootScope.idle.isIDLE == isIDLE) { + return; + } + + // dLog('IDLE changed', isIDLE); + $rootScope.$apply(function () { + $rootScope.idle.isIDLE = isIDLE; + }); + + if (isIDLE && e.type == 'timeout') { + $($window).on('mousemove', onEvent); + } + } +}) + + +.service('NotificationsManager', function ($rootScope, $window, $timeout, $interval, IdleManager) { + + var notificationsUiSupport = window.webkitNotifications !== undefined; + var notificationsShown = []; + var notificationsCount = 0; + var titleBackup = document.title, + titlePromise; + + $rootScope.$watch('idle.isIDLE', function (newVal) { + // dLog('isIDLE watch', newVal); + $interval.cancel(titlePromise); + + if (!newVal) { + notificationsCount = 0; + document.title = titleBackup; + notificationsClear(); + } else { + titleBackup = document.title; + + titlePromise = $interval(function () { + var time = +new Date(); + // dLog('check title', notificationsCount, time % 2000 > 1000); + if (!notificationsCount || time % 2000 > 1000) { + document.title = titleBackup; + } else { + document.title = notificationsCount + ' notifications'; + } + }, 1000); + } + }); + + return { + start: start, + notify: notify + }; + + function start () { + if (!notificationsUiSupport) { + return false; + } + + var havePermission = window.webkitNotifications.checkPermission(); + // dLog('perm', havePermission); + + if (havePermission != 0) { // 0 is PERMISSION_ALLOWED + $($window).on('click', requestPermission); + } + + $($window).on('beforeunload', notificationsClear); + } + + function requestPermission() { + window.webkitNotifications.requestPermission(); + $($window).off('click', requestPermission); + } + + function notify (data) { + // dLog('notify', $rootScope.idle.isIDLE); + if (!$rootScope.idle.isIDLE) { + return false; + } + + notificationsCount++; + + if (!notificationsUiSupport || + window.webkitNotifications.checkPermission() != 0) { + return false; + } + + var notification = window.webkitNotifications.createNotification( + data.image || '', + data.title || '', + data.message || '' + ); + + notification.onclick = function () { + notification.close(); + window.focus(); + notificationsClear(); + if (data.onclick) { + data.onclick(); + } + }; + + // dLog('notify', notification); + + notification.show(); + + notificationsShown.push(notification); + }; + + function notificationsClear() { + angular.forEach(notificationsShown, function (notification) { + notification.close(); + }); + notificationsShown = []; + } +}) + + + diff --git a/app/partials/message.html b/app/partials/message.html index 7604c8a9..0a8cae6f 100644 --- a/app/partials/message.html +++ b/app/partials/message.html @@ -19,10 +19,10 @@ removed group photo - invited + invited - kicked + kicked @@ -51,7 +51,7 @@
-
+
@@ -68,9 +68,10 @@
- + +
{{historyMessage.media.document.size | formatSize}}
-
{{historyMessage.media.document.file_name}} {{historyMessage.media.document.size | formatSize}}
+
{{historyMessage.media.document.file_name}}
@@ -95,8 +96,9 @@
+
{{historyMessage.media.size | formatSize}}
-
{{historyMessage.media.file_name}} {{historyMessage.media.size | formatSize}}
+
{{historyMessage.media.file_name}}