Browse Source

Added desktop notififactions, added download/upload progress

master
Igor Zhukov 11 years ago
parent
commit
8d1f2706a5
  1. 45
      app/css/app.css
  2. 10
      app/index.html
  3. 62
      app/js/controllers.js
  4. 19
      app/js/directives.js
  5. 6
      app/js/lib/mtproto.js
  6. 213
      app/js/services.js
  7. 14
      app/partials/message.html

45
app/css/app.css

@ -715,6 +715,8 @@ div.im_message_video_thumb { @@ -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 { @@ -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 { @@ -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 { @@ -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 {
}*/

10
app/index.html

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
<link rel="stylesheet" href="vendor/angular/angular-csp.css"/>
<link rel="stylesheet" href="vendor/bootstrap/css/bootstrap.css"/>
<link rel="stylesheet" href="vendor/jquery.nanoscroller/nanoscroller.css"/>
<link rel="stylesheet" href="css/app.css"/>
<link rel="stylesheet" href="css/app.css?1"/>
</head>
<body>
@ -36,10 +36,10 @@ @@ -36,10 +36,10 @@
<script type="text/javascript" src="js/util.js"></script>
<script type="text/javascript" src="js/app.js"></script>
<script type="text/javascript" src="js/services.js"></script>
<script type="text/javascript" src="js/controllers.js"></script>
<script type="text/javascript" src="js/filters.js"></script>
<script type="text/javascript" src="js/directives.js"></script>
<script type="text/javascript" src="js/services.js?1"></script>
<script type="text/javascript" src="js/controllers.js?1"></script>
<script type="text/javascript" src="js/filters.js?1"></script>
<script type="text/javascript" src="js/directives.js?1"></script>
</body>
</html>

62
app/js/controllers.js

@ -207,12 +207,14 @@ angular.module('myApp.controllers', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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) {

19
app/js/directives.js

@ -25,7 +25,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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);
});

6
app/js/lib/mtproto.js

@ -1566,10 +1566,10 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato @@ -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);

213
app/js/services.js

@ -405,7 +405,7 @@ angular.module('myApp.services', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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 = [];
}
})

14
app/partials/message.html

@ -19,10 +19,10 @@ @@ -19,10 +19,10 @@
removed group photo
</span>
<span ng-switch-when="messageActionChatAddUser">
invited <span ng-bind-html="historyMessage.action.user.rFullName"></span>
invited <a ng-click="openUser(historyMessage.action.user_id)" ng-bind-html="historyMessage.action.user.rFullName"></a>
</span>
<span ng-switch-when="messageActionChatDeleteUser">
kicked <span ng-bind-html="historyMessage.action.user.rFullName"></span>
kicked <a ng-click="openUser(historyMessage.action.user_id)" ng-bind-html="historyMessage.action.user.rFullName"></a>
</span>
<span ng-switch-default>
@ -51,7 +51,7 @@ @@ -51,7 +51,7 @@
<div class="im_message_body">
<div class="im_message_author" ng-bind-html="historyMessage.fromUser.rFirstName"></div>
<a class="im_message_author" ng-click="openUser(historyMessage.from_id)" ng-bind-html="historyMessage.fromUser.rFirstName"></a>
<div class="im_message_media" ng-if="historyMessage.media &amp;&amp; historyMessage.media._ != 'messageMediaEmpty'" ng-switch="historyMessage.media._">
@ -68,9 +68,10 @@ @@ -68,9 +68,10 @@
</a>
<div ng-switch-when="messageMediaDocument">
<a class="im_message_document" href="" ng-click="openDoc(historyMessage.media.document.id)">
<a class="im_message_document" href="" ng-click="openDoc(historyMessage.media.document.id)">
<div class="im_message_document_size">{{historyMessage.media.document.size | formatSize}}</div>
<i class="icon icon-document"></i>
<div class="im_message_document_name"><strong>{{historyMessage.media.document.file_name}}</strong> {{historyMessage.media.document.size | formatSize}}</div>
<div class="im_message_document_name">{{historyMessage.media.document.file_name}}</div>
</a>
<div class="im_message_download_progress_wrap" ng-if="historyMessage.media.document.progress.enabled">
@ -95,8 +96,9 @@ @@ -95,8 +96,9 @@
</div>
<div ng-switch-when="messageMediaPending" class="im_message_upload_file im_message_upload_{{historyMessage.media.type}}">
<div class="im_message_document_size">{{historyMessage.media.size | formatSize}}</div>
<i class="icon icon-document"></i>
<div class="im_message_document_name"><strong>{{historyMessage.media.file_name}}</strong> {{historyMessage.media.size | formatSize}}</div>
<div class="im_message_document_name">{{historyMessage.media.file_name}}</div>
<div class="im_message_upload_progress_wrap">
<div class="progress tg_up_progress">

Loading…
Cancel
Save