2018-05-09 22:12:31 +00:00
|
|
|
|
/* global Signal:false */
|
2018-05-02 20:43:32 +00:00
|
|
|
|
/* global Backbone: false */
|
|
|
|
|
|
2018-05-02 21:58:48 +00:00
|
|
|
|
/* global drawAttention: false */
|
2018-05-02 20:43:32 +00:00
|
|
|
|
/* global i18n: false */
|
2018-05-02 21:58:48 +00:00
|
|
|
|
/* global isFocused: false */
|
2018-05-02 20:43:32 +00:00
|
|
|
|
/* global Signal: false */
|
|
|
|
|
/* global storage: false */
|
|
|
|
|
/* global Whisper: false */
|
2018-05-11 00:07:42 +00:00
|
|
|
|
/* global _: false */
|
2018-05-02 20:43:32 +00:00
|
|
|
|
|
|
|
|
|
// eslint-disable-next-line func-names
|
2018-04-27 21:25:04 +00:00
|
|
|
|
(function() {
|
|
|
|
|
'use strict';
|
2018-05-02 20:43:32 +00:00
|
|
|
|
|
2018-04-27 21:25:04 +00:00
|
|
|
|
window.Whisper = window.Whisper || {};
|
2018-05-02 20:43:32 +00:00
|
|
|
|
const { Settings } = Signal.Types;
|
2015-03-17 22:06:21 +00:00
|
|
|
|
|
2018-05-02 22:02:49 +00:00
|
|
|
|
const SettingNames = {
|
2018-04-27 21:25:04 +00:00
|
|
|
|
COUNT: 'count',
|
|
|
|
|
NAME: 'name',
|
|
|
|
|
MESSAGE: 'message',
|
|
|
|
|
};
|
2016-02-18 00:08:17 +00:00
|
|
|
|
|
2019-03-18 19:10:56 +00:00
|
|
|
|
function filter(text) {
|
|
|
|
|
return (text || '')
|
|
|
|
|
.replace(/&/g, '&')
|
|
|
|
|
.replace(/"/g, '"')
|
|
|
|
|
.replace(/'/g, ''')
|
|
|
|
|
.replace(/</g, '<')
|
|
|
|
|
.replace(/>/g, '>');
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-27 21:25:04 +00:00
|
|
|
|
Whisper.Notifications = new (Backbone.Collection.extend({
|
2018-05-02 20:43:32 +00:00
|
|
|
|
initialize() {
|
2018-04-27 21:25:04 +00:00
|
|
|
|
this.isEnabled = false;
|
|
|
|
|
this.on('add', this.update);
|
|
|
|
|
this.on('remove', this.onRemove);
|
2018-05-09 22:12:31 +00:00
|
|
|
|
|
|
|
|
|
this.lastNotification = null;
|
2018-05-11 00:07:42 +00:00
|
|
|
|
|
|
|
|
|
// Testing indicated that trying to create/destroy notifications too quickly
|
|
|
|
|
// resulted in notifications that stuck around forever, requiring the user
|
|
|
|
|
// to manually close them. This introduces a minimum amount of time between calls,
|
|
|
|
|
// and batches up the quick successive update() calls we get from an incoming
|
|
|
|
|
// read sync, which might have a number of messages referenced inside of it.
|
2018-05-11 00:27:22 +00:00
|
|
|
|
this.fastUpdate = this.update;
|
2018-05-11 00:07:42 +00:00
|
|
|
|
this.update = _.debounce(this.update, 1000);
|
2018-04-27 21:25:04 +00:00
|
|
|
|
},
|
2018-05-02 20:43:32 +00:00
|
|
|
|
update() {
|
2018-05-11 00:27:22 +00:00
|
|
|
|
if (this.lastNotification) {
|
|
|
|
|
this.lastNotification.close();
|
|
|
|
|
this.lastNotification = null;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-27 21:25:04 +00:00
|
|
|
|
const { isEnabled } = this;
|
2018-05-02 21:58:48 +00:00
|
|
|
|
const isAppFocused = isFocused();
|
2018-04-27 21:25:04 +00:00
|
|
|
|
const isAudioNotificationEnabled =
|
|
|
|
|
storage.get('audio-notification') || false;
|
|
|
|
|
const isAudioNotificationSupported = Settings.isAudioNotificationSupported();
|
2018-05-04 19:37:38 +00:00
|
|
|
|
const isNotificationGroupingSupported = Settings.isNotificationGroupingSupported();
|
2018-04-27 21:25:04 +00:00
|
|
|
|
const numNotifications = this.length;
|
2018-05-02 22:04:00 +00:00
|
|
|
|
const userSetting = this.getUserSetting();
|
|
|
|
|
|
|
|
|
|
const status = Signal.Notifications.getStatus({
|
|
|
|
|
isAppFocused,
|
|
|
|
|
isAudioNotificationEnabled,
|
|
|
|
|
isAudioNotificationSupported,
|
2018-05-02 22:33:58 +00:00
|
|
|
|
isEnabled,
|
2018-04-27 21:25:04 +00:00
|
|
|
|
numNotifications,
|
2018-05-02 22:04:00 +00:00
|
|
|
|
userSetting,
|
2018-04-27 21:25:04 +00:00
|
|
|
|
});
|
2018-02-23 21:25:11 +00:00
|
|
|
|
|
2018-07-21 19:00:08 +00:00
|
|
|
|
window.log.info(
|
2018-05-04 19:37:38 +00:00
|
|
|
|
'Update notifications:',
|
|
|
|
|
Object.assign({}, status, {
|
|
|
|
|
isNotificationGroupingSupported,
|
|
|
|
|
})
|
|
|
|
|
);
|
2018-03-02 19:43:03 +00:00
|
|
|
|
|
2018-05-02 22:04:00 +00:00
|
|
|
|
if (status.type !== 'ok') {
|
|
|
|
|
if (status.shouldClearNotifications) {
|
2018-05-11 00:27:22 +00:00
|
|
|
|
this.reset([]);
|
2018-05-02 22:04:00 +00:00
|
|
|
|
}
|
2017-09-29 16:15:28 +00:00
|
|
|
|
|
2018-04-27 21:25:04 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-02-18 00:08:17 +00:00
|
|
|
|
|
2018-05-02 20:43:32 +00:00
|
|
|
|
let title;
|
|
|
|
|
let message;
|
|
|
|
|
let iconUrl;
|
2017-04-28 01:31:35 +00:00
|
|
|
|
|
2018-04-27 21:25:04 +00:00
|
|
|
|
// NOTE: i18n has more complex rules for pluralization than just
|
|
|
|
|
// distinguishing between zero (0) and other (non-zero),
|
|
|
|
|
// e.g. Russian:
|
|
|
|
|
// http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html
|
2018-05-09 22:12:31 +00:00
|
|
|
|
const newMessageCountLabel = `${numNotifications} ${
|
|
|
|
|
numNotifications === 1 ? i18n('newMessage') : i18n('newMessages')
|
|
|
|
|
}`;
|
2016-02-18 00:08:17 +00:00
|
|
|
|
|
2018-05-09 22:12:31 +00:00
|
|
|
|
const last = this.last().toJSON();
|
2018-05-02 22:04:00 +00:00
|
|
|
|
switch (userSetting) {
|
2018-05-02 22:02:49 +00:00
|
|
|
|
case SettingNames.COUNT:
|
2018-04-27 21:25:04 +00:00
|
|
|
|
title = 'Signal';
|
2018-05-09 22:12:31 +00:00
|
|
|
|
message = newMessageCountLabel;
|
2018-04-27 21:25:04 +00:00
|
|
|
|
break;
|
2018-05-09 22:12:31 +00:00
|
|
|
|
case SettingNames.NAME: {
|
|
|
|
|
const lastMessageTitle = last.title;
|
|
|
|
|
title = newMessageCountLabel;
|
|
|
|
|
// eslint-disable-next-line prefer-destructuring
|
|
|
|
|
iconUrl = last.iconUrl;
|
|
|
|
|
if (numNotifications === 1) {
|
|
|
|
|
message = `${i18n('notificationFrom')} ${lastMessageTitle}`;
|
|
|
|
|
} else {
|
|
|
|
|
message = `${i18n(
|
|
|
|
|
'notificationMostRecentFrom'
|
|
|
|
|
)} ${lastMessageTitle}`;
|
|
|
|
|
}
|
2018-04-27 21:25:04 +00:00
|
|
|
|
break;
|
2018-05-09 22:12:31 +00:00
|
|
|
|
}
|
2018-05-02 22:02:49 +00:00
|
|
|
|
case SettingNames.MESSAGE:
|
2018-04-27 21:25:04 +00:00
|
|
|
|
if (numNotifications === 1) {
|
2018-05-09 22:12:31 +00:00
|
|
|
|
// eslint-disable-next-line prefer-destructuring
|
|
|
|
|
title = last.title;
|
|
|
|
|
// eslint-disable-next-line prefer-destructuring
|
|
|
|
|
message = last.message;
|
2018-04-27 21:25:04 +00:00
|
|
|
|
} else {
|
2018-05-09 22:12:31 +00:00
|
|
|
|
title = newMessageCountLabel;
|
|
|
|
|
message = `${i18n('notificationMostRecent')} ${last.message}`;
|
2018-04-27 21:25:04 +00:00
|
|
|
|
}
|
2018-05-09 22:12:31 +00:00
|
|
|
|
// eslint-disable-next-line prefer-destructuring
|
|
|
|
|
iconUrl = last.iconUrl;
|
2018-04-27 21:25:04 +00:00
|
|
|
|
break;
|
2018-05-02 20:43:32 +00:00
|
|
|
|
default:
|
2018-07-21 19:00:08 +00:00
|
|
|
|
window.log.error(
|
2018-05-09 22:12:31 +00:00
|
|
|
|
`Error: Unknown user notification setting: '${userSetting}'`
|
|
|
|
|
);
|
2018-05-02 20:43:32 +00:00
|
|
|
|
break;
|
2018-04-27 21:25:04 +00:00
|
|
|
|
}
|
2017-09-29 16:15:28 +00:00
|
|
|
|
|
2018-05-09 22:12:31 +00:00
|
|
|
|
const shouldHideExpiringMessageBody =
|
|
|
|
|
last.isExpiringMessage && Signal.OS.isMacOS();
|
|
|
|
|
if (shouldHideExpiringMessageBody) {
|
|
|
|
|
message = i18n('newMessage');
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-02 22:04:00 +00:00
|
|
|
|
drawAttention();
|
|
|
|
|
|
2019-03-14 18:58:26 +00:00
|
|
|
|
this.lastNotification = new Notification(title, {
|
2019-03-18 19:10:56 +00:00
|
|
|
|
body: window.platform === 'linux' ? filter(message) : message,
|
2018-05-02 23:06:03 +00:00
|
|
|
|
icon: iconUrl,
|
|
|
|
|
silent: !status.shouldPlayNotificationSound,
|
|
|
|
|
});
|
2019-03-14 18:58:26 +00:00
|
|
|
|
this.lastNotification.onclick = () =>
|
2019-01-14 21:49:58 +00:00
|
|
|
|
this.trigger('click', last.conversationId, last.id);
|
2017-09-29 16:15:28 +00:00
|
|
|
|
|
2018-05-09 22:12:31 +00:00
|
|
|
|
// We continue to build up more and more messages for our notifications
|
|
|
|
|
// until the user comes back to our app or closes the app. Then we’ll
|
|
|
|
|
// clear everything out. The good news is that we'll have a maximum of
|
|
|
|
|
// 1 notification in the Notification area (something like
|
|
|
|
|
// ‘10 new messages’) assuming that `Notification::close` does its job.
|
2018-04-27 21:25:04 +00:00
|
|
|
|
},
|
2018-05-02 22:04:00 +00:00
|
|
|
|
getUserSetting() {
|
2018-05-02 22:02:49 +00:00
|
|
|
|
return storage.get('notification-setting') || SettingNames.MESSAGE;
|
2018-04-27 21:25:04 +00:00
|
|
|
|
},
|
2018-05-02 20:43:32 +00:00
|
|
|
|
onRemove() {
|
2018-07-21 19:00:08 +00:00
|
|
|
|
window.log.info('Remove notification');
|
2018-05-11 00:27:22 +00:00
|
|
|
|
this.update();
|
2018-04-27 21:25:04 +00:00
|
|
|
|
},
|
2018-05-02 20:43:32 +00:00
|
|
|
|
clear() {
|
2018-07-21 19:00:08 +00:00
|
|
|
|
window.log.info('Remove all notifications');
|
2018-04-27 21:25:04 +00:00
|
|
|
|
this.reset([]);
|
2018-05-11 00:27:22 +00:00
|
|
|
|
this.update();
|
|
|
|
|
},
|
|
|
|
|
// We don't usually call this, but when the process is shutting down, we should at
|
|
|
|
|
// least try to remove the notification immediately instead of waiting for the
|
|
|
|
|
// normal debounce.
|
|
|
|
|
fastClear() {
|
|
|
|
|
this.reset([]);
|
|
|
|
|
this.fastUpdate();
|
2018-04-27 21:25:04 +00:00
|
|
|
|
},
|
2018-05-02 20:43:32 +00:00
|
|
|
|
enable() {
|
2018-04-27 21:25:04 +00:00
|
|
|
|
const needUpdate = !this.isEnabled;
|
|
|
|
|
this.isEnabled = true;
|
|
|
|
|
if (needUpdate) {
|
|
|
|
|
this.update();
|
|
|
|
|
}
|
|
|
|
|
},
|
2018-05-02 20:43:32 +00:00
|
|
|
|
disable() {
|
2018-04-27 21:25:04 +00:00
|
|
|
|
this.isEnabled = false;
|
|
|
|
|
},
|
|
|
|
|
}))();
|
2015-03-17 22:06:21 +00:00
|
|
|
|
})();
|