
Creating/destroying notifications too quickly in testing on macOS would result in them sticking around forever, requiring manual user dismissal. We want to dismiss them for the user when we close or our window is activated. So now we debounce() calls to our notifications code.
187 lines
5.7 KiB
JavaScript
187 lines
5.7 KiB
JavaScript
/* global Signal:false */
|
||
/* global Backbone: false */
|
||
|
||
/* global ConversationController: false */
|
||
/* global drawAttention: false */
|
||
/* global i18n: false */
|
||
/* global isFocused: false */
|
||
/* global Signal: false */
|
||
/* global storage: false */
|
||
/* global Whisper: false */
|
||
/* global _: false */
|
||
|
||
// eslint-disable-next-line func-names
|
||
(function() {
|
||
'use strict';
|
||
|
||
window.Whisper = window.Whisper || {};
|
||
const { Settings } = Signal.Types;
|
||
|
||
const SettingNames = {
|
||
COUNT: 'count',
|
||
NAME: 'name',
|
||
MESSAGE: 'message',
|
||
};
|
||
|
||
Whisper.Notifications = new (Backbone.Collection.extend({
|
||
initialize() {
|
||
this.isEnabled = false;
|
||
this.on('add', this.update);
|
||
this.on('remove', this.onRemove);
|
||
|
||
this.lastNotification = null;
|
||
|
||
// 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.
|
||
this.update = _.debounce(this.update, 1000);
|
||
},
|
||
onClick(conversationId) {
|
||
const conversation = ConversationController.get(conversationId);
|
||
this.trigger('click', conversation);
|
||
},
|
||
update() {
|
||
const { isEnabled } = this;
|
||
const isAppFocused = isFocused();
|
||
const isAudioNotificationEnabled =
|
||
storage.get('audio-notification') || false;
|
||
const isAudioNotificationSupported = Settings.isAudioNotificationSupported();
|
||
const isNotificationGroupingSupported = Settings.isNotificationGroupingSupported();
|
||
const numNotifications = this.length;
|
||
const userSetting = this.getUserSetting();
|
||
|
||
const status = Signal.Notifications.getStatus({
|
||
isAppFocused,
|
||
isAudioNotificationEnabled,
|
||
isAudioNotificationSupported,
|
||
isEnabled,
|
||
numNotifications,
|
||
userSetting,
|
||
});
|
||
|
||
console.log(
|
||
'Update notifications:',
|
||
Object.assign({}, status, {
|
||
isNotificationGroupingSupported,
|
||
})
|
||
);
|
||
|
||
if (status.type !== 'ok') {
|
||
if (status.shouldClearNotifications) {
|
||
this.clear();
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
let title;
|
||
let message;
|
||
let iconUrl;
|
||
|
||
// 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
|
||
const newMessageCountLabel = `${numNotifications} ${
|
||
numNotifications === 1 ? i18n('newMessage') : i18n('newMessages')
|
||
}`;
|
||
|
||
const last = this.last().toJSON();
|
||
switch (userSetting) {
|
||
case SettingNames.COUNT:
|
||
title = 'Signal';
|
||
message = newMessageCountLabel;
|
||
break;
|
||
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}`;
|
||
}
|
||
break;
|
||
}
|
||
case SettingNames.MESSAGE:
|
||
if (numNotifications === 1) {
|
||
// eslint-disable-next-line prefer-destructuring
|
||
title = last.title;
|
||
// eslint-disable-next-line prefer-destructuring
|
||
message = last.message;
|
||
} else {
|
||
title = newMessageCountLabel;
|
||
message = `${i18n('notificationMostRecent')} ${last.message}`;
|
||
}
|
||
// eslint-disable-next-line prefer-destructuring
|
||
iconUrl = last.iconUrl;
|
||
break;
|
||
default:
|
||
console.log(
|
||
`Error: Unknown user notification setting: '${userSetting}'`
|
||
);
|
||
break;
|
||
}
|
||
|
||
const shouldHideExpiringMessageBody =
|
||
last.isExpiringMessage && Signal.OS.isMacOS();
|
||
if (shouldHideExpiringMessageBody) {
|
||
message = i18n('newMessage');
|
||
}
|
||
|
||
drawAttention();
|
||
|
||
if (this.lastNotification) {
|
||
this.lastNotification.close();
|
||
}
|
||
const notification = new Notification(title, {
|
||
body: message,
|
||
icon: iconUrl,
|
||
tag: isNotificationGroupingSupported ? 'signal' : undefined,
|
||
silent: !status.shouldPlayNotificationSound,
|
||
});
|
||
notification.onclick = () => this.onClick(last.conversationId);
|
||
this.lastNotification = notification;
|
||
|
||
// 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.
|
||
},
|
||
getUserSetting() {
|
||
return storage.get('notification-setting') || SettingNames.MESSAGE;
|
||
},
|
||
onRemove() {
|
||
console.log('Remove notification');
|
||
if (this.length === 0) {
|
||
this.clear();
|
||
} else {
|
||
this.update();
|
||
}
|
||
},
|
||
clear() {
|
||
console.log('Remove all notifications');
|
||
if (this.lastNotification) {
|
||
this.lastNotification.close();
|
||
}
|
||
this.reset([]);
|
||
},
|
||
enable() {
|
||
const needUpdate = !this.isEnabled;
|
||
this.isEnabled = true;
|
||
if (needUpdate) {
|
||
this.update();
|
||
}
|
||
},
|
||
disable() {
|
||
this.isEnabled = false;
|
||
},
|
||
}))();
|
||
})();
|