diff --git a/app/js/controllers.js b/app/js/controllers.js index 336b15df..15a52b45 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -2919,22 +2919,20 @@ angular.module('myApp.controllers', ['myApp.i18n']) }); }; - Storage.get('notify_nodesktop', 'notify_nosound', 'send_ctrlenter', 'notify_volume', 'notify_novibrate', 'notify_nopreview').then(function (settings) { + Storage.get('notify_nodesktop', 'send_ctrlenter', 'notify_volume', 'notify_novibrate', 'notify_nopreview').then(function (settings) { $scope.notify.desktop = !settings[0]; - $scope.send.enter = settings[2] ? '' : '1'; + $scope.send.enter = settings[1] ? '' : '1'; - if (settings[1]) { - $scope.notify.volume = 0; - } else if (settings[3] !== false) { - $scope.notify.volume = settings[3] > 0 && settings[3] <= 1.0 ? settings[3] : 0; + if (settings[2] !== false) { + $scope.notify.volume = settings[2] > 0 && settings[2] <= 1.0 ? settings[2] : 0; } else { $scope.notify.volume = 0.5; } $scope.notify.canVibrate = NotificationsManager.getVibrateSupport(); - $scope.notify.vibrate = !settings[4]; + $scope.notify.vibrate = !settings[3]; - $scope.notify.preview = !settings[5]; + $scope.notify.preview = !settings[4]; $scope.notify.volumeOf4 = function () { return 1 + Math.ceil(($scope.notify.volume - 0.1) / 0.33); @@ -2952,7 +2950,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.$watch('notify.volume', function (newValue, oldValue) { if (newValue !== oldValue) { Storage.set({notify_volume: newValue}); - Storage.remove('notify_nosound'); + $rootScope.$broadcast('settings_changed'); NotificationsManager.clear(); if (testSoundPromise) { @@ -2972,6 +2970,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) } else { Storage.set({notify_nodesktop: true}); } + $rootScope.$broadcast('settings_changed'); } $scope.togglePreview = function () { @@ -2982,6 +2981,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) } else { Storage.set({notify_nopreview: true}); } + $rootScope.$broadcast('settings_changed'); } $scope.toggleVibrate = function () { @@ -2992,6 +2992,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) } else { Storage.set({notify_novibrate: true}); } + $rootScope.$broadcast('settings_changed'); } $scope.toggleCtrlEnter = function (newValue) { diff --git a/app/js/locales/en-us.json b/app/js/locales/en-us.json index 073b7511..f9e9f910 100644 --- a/app/js/locales/en-us.json +++ b/app/js/locales/en-us.json @@ -255,6 +255,7 @@ "conversation_kicked_user_message": "removed user", "conversation_joined_by_link": "joined group", "conversation_message_sent": "sent you a message", + "conversation_forwarded_X_messages": "{'one': 'forwarded {} message', 'other': 'forwarded {} messages'}", "conversation_unknown_user": "Somebody", "conversation_unknown_chat": "Unknown chat", diff --git a/app/js/services.js b/app/js/services.js index 046fc003..9c469957 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -840,6 +840,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) midnightOffseted = new Date(), midnightOffset; + Storage.get('server_time_offset').then(function (to) { + if (to) { + serverTimeOffset = to; + } + }); var maxSeenID = false; if (Config.Modes.packed) { @@ -848,13 +853,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }); } - Storage.get('server_time_offset').then(function (to) { - if (to) { - serverTimeOffset = to; - } - }); var dateOrTimeFilter = $filter('dateOrTime'); + var fwdMessagesPluralize = _.pluralize('conversation_forwarded_X_messages'); midnightOffseted.setHours(0); midnightOffseted.setMinutes(0); @@ -966,9 +967,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) if (message.unread && !message.out) { NotificationsManager.getPeerMuted(notifyPeer).then(function (muted) { if (!muted) { - Storage.get('notify_nopreview').then(function (no_preview) { - notifyAboutMessage(message, no_preview); - }); + notifyAboutMessage(message); } }); } @@ -1375,6 +1374,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } } + NotificationsManager.soundReset(AppPeersManager.getPeerString(peerID)) + return historyStorage.readPromise; } @@ -2255,7 +2256,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } } - function notifyAboutMessage (message, no_preview) { + function notifyAboutMessage (message, options) { + options = options || {}; + var peerID = getMessagePeer(message); var fromUser = AppUsersManager.getUser(message.from_id); var fromPhoto = AppUsersManager.getUserPhoto(message.from_id, 'User'); @@ -2264,8 +2267,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) notificationMessage = false, notificationPhoto; - if (message.message) { - if (no_preview) { + var notifySettings = NotificationsManager.getNotifySettings(); + + if (message.fwd_from_id && options.fwd_count) { + notificationMessage = fwdMessagesPluralize(options.fwd_count); + } else if (message.message) { + if (notifySettings.nopreview) { notificationMessage = _('conversation_message_sent'); } else { notificationMessage = RichTextProcessor.wrapPlainText(message.message); @@ -2303,6 +2310,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) case 'messageActionChatDeleteUser': notificationMessage = message.action.user_id == message.from_id ? _('conversation_left_group') : _('conversation_kicked_user_message_raw'); break; + case 'messageActionChatJoinedByLink': + notificationMessage = _('conversation_joined_by_link'); + break; } } @@ -2380,6 +2390,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var newMessagesToHandle = {}; var newDialogsHandlePromise = false; var newDialogsToHandle = {}; + var notificationsHandlePromise = false; + var notificationsToHandle = {}; function handleNewMessages () { $timeout.cancel(newMessagesHandlePromise); @@ -2395,6 +2407,31 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) newDialogsToHandle = {}; } + function handleNotifications () { + $timeout.cancel(notificationsHandlePromise); + notificationsHandlePromise = false; + + var timeout = $rootScope.idle.isIDLE && StatusManager.isOtherDeviceActive() ? 30000 : 1000; + angular.forEach(notificationsToHandle, function (notifyPeerToHandle) { + notifyPeerToHandle.isMutedPromise.then(function (muted) { + var topMessage = notifyPeerToHandle.top_message; + if (muted || + !topMessage.unread) { + return; + } + setTimeout(function () { + if (topMessage.unread) { + notifyAboutMessage(topMessage, { + fwd_count: notifyPeerToHandle.fwd_count + }); + } + }, timeout); + }); + }); + + notificationsToHandle = {}; + } + $rootScope.$on('apiUpdate', function (e, update) { // if (update._ != 'updateUserStatus') { // console.log('on apiUpdate', update); @@ -2490,17 +2527,28 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) if (inboxUnread && ($rootScope.selectedPeerID != peerID || $rootScope.idle.isIDLE)) { var notifyPeer = message.flags & 16 ? message.from_id : peerID; - var isMutedPromise = NotificationsManager.getPeerMuted(notifyPeer); - var timeout = $rootScope.idle.isIDLE && StatusManager.isOtherDeviceActive() ? 30000 : 1000; - setTimeout(function () { - isMutedPromise.then(function (muted) { - if (message.unread && !muted) { - Storage.get('notify_nopreview').then(function (no_preview) { - notifyAboutMessage(message, no_preview); - }); - } - }) - }, timeout); + var notifyPeerToHandle = notificationsToHandle[notifyPeer]; + if (notifyPeerToHandle === undefined) { + notifyPeerToHandle = notificationsToHandle[notifyPeer] = { + isMutedPromise: NotificationsManager.getPeerMuted(notifyPeer), + fwd_count: 0, + from_id: 0 + }; + } + + if (notifyPeerToHandle.from_id != message.from_id) { + notifyPeerToHandle.from_id = message.from_id; + notifyPeerToHandle.fwd_count = 0; + } + if (message.fwd_from_id) { + notifyPeerToHandle.fwd_count++; + } + + notifyPeerToHandle.top_message = message; + + if (!notificationsHandlePromise) { + notificationsHandlePromise = $timeout(handleNotifications, 1000); + } } incrementMaxSeenID(message.id); @@ -4522,6 +4570,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var notificationsShown = {}; var notificationIndex = 0; var notificationsCount = 0; + var soundsPlayed = {}; var vibrateSupport = !!navigator.vibrate; var nextSoundAt = false; var prevSoundVolume = false; @@ -4534,6 +4583,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) titlePromise; var prevFavicon; + var settings = {}; + $rootScope.$watch('idle.isIDLE', function (newVal) { if (!newVal) { notificationsClear(); @@ -4596,14 +4647,34 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) notify: notify, cancel: notificationCancel, clear: notificationsClear, + soundReset: notificationSoundReset, getPeerSettings: getPeerSettings, getPeerMuted: getPeerMuted, savePeerSettings: savePeerSettings, updatePeerSettings: updatePeerSettings, + updateNotifySettings: updateNotifySettings, + getNotifySettings: getNotifySettings, getVibrateSupport: getVibrateSupport, testSound: playSound }; + function updateNotifySettings () { + Storage.get('notify_nodesktop', 'notify_volume', 'notify_novibrate', 'notify_nopreview').then(function (updSettings) { + + settings.nodesktop = updSettings[0]; + settings.volume = updSettings[1] === false + ? 0.5 + : updSettings[1]; + + settings.novibrate = updSettings[2]; + settings.nopreview = updSettings[3]; + }); + } + + function getNotifySettings () { + return settings; + } + function getPeerSettings (peerID) { if (peerSettings[peerID] !== undefined) { return peerSettings[peerID]; @@ -4660,7 +4731,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } function start () { + updateNotifySettings(); + $rootScope.$on('settings_changed', updateNotifySettings); registerDevice(); + if (!notificationsUiSupport) { return false; } @@ -4695,70 +4769,75 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) notificationsCount++; - Storage.get('notify_nosound', 'notify_volume').then(function (settings) { - if (!settings[0] && settings[1] === false || settings[1] > 0) { - playSound(settings[1] || 0.5); - } - }) + var now = tsNow(); + if (settings.volume > 0 && + ( + !data.tag || + !soundsPlayed[data.tag] || + now > soundsPlayed[data.tag] + 60000 + ) + ) { + playSound(settings.volume); + soundsPlayed[data.tag] = now; + } if (!notificationsUiSupport || 'Notification' in window && Notification.permission !== 'granted') { return false; } - Storage.get('notify_nodesktop', 'notify_novibrate').then(function (settings) { - if (settings[0]) { - if (vibrateSupport && !settings[1]) { - navigator.vibrate([200, 100, 200]); - return; - } - return; - } - var idx = ++notificationIndex, - key = data.key || 'k' + idx, - notification; - - if ('Notification' in window) { - notification = new Notification(data.title, { - icon: data.image || '', - body: data.message || '', - tag: data.tag || '' - }); - } - else if ('mozNotification' in navigator) { - notification = navigator.mozNotification.createNotification(data.title, data.message || '', data.image || ''); - } - else if (notificationsMsSiteMode) { - window.external.msSiteModeClearIconOverlay(); - window.external.msSiteModeSetIconOverlay('img/icons/icon16.png', data.title); - window.external.msSiteModeActivate(); - notification = { - index: idx - }; - } - else { + if (settings.nodesktop) { + if (vibrateSupport && !settings.novibrate) { + navigator.vibrate([200, 100, 200]); return; } + return; + } - notification.onclick = function () { - notification.close(); - AppRuntimeManager.focus(); - notificationsClear(); - if (data.onclick) { - data.onclick(); - } - }; + var idx = ++notificationIndex, + key = data.key || 'k' + idx, + notification; - notification.onclose = function () { - delete notificationsShown[key]; - notificationsClear(); + if ('Notification' in window) { + notification = new Notification(data.title, { + icon: data.image || '', + body: data.message || '', + tag: data.tag || '' + }); + } + else if ('mozNotification' in navigator) { + notification = navigator.mozNotification.createNotification(data.title, data.message || '', data.image || ''); + } + else if (notificationsMsSiteMode) { + window.external.msSiteModeClearIconOverlay(); + window.external.msSiteModeSetIconOverlay('img/icons/icon16.png', data.title); + window.external.msSiteModeActivate(); + notification = { + index: idx }; + } + else { + return; + } - if (notification.show) { - notification.show(); + notification.onclick = function () { + notification.close(); + AppRuntimeManager.focus(); + notificationsClear(); + if (data.onclick) { + data.onclick(); } - notificationsShown[key] = notification; - }); + }; + + notification.onclose = function () { + delete notificationsShown[key]; + notificationsClear(); + }; + + if (notification.show) { + notification.show(); + } + notificationsShown[key] = notification; }; function playSound (volume) { @@ -4795,6 +4874,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } } + function notificationSoundReset (tag) { + delete soundsPlayed[tag]; + } + function notificationsClear() { if (notificationsMsSiteMode) { window.external.msSiteModeClearIconOverlay(); diff --git a/app/partials/desktop/reply_message.html b/app/partials/desktop/reply_message.html index 41e11be6..4735a72f 100644 --- a/app/partials/desktop/reply_message.html +++ b/app/partials/desktop/reply_message.html @@ -49,6 +49,9 @@ + + +