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 @@
+
+
+