diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 90634ebeda1..e14ea83df62 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1651,6 +1651,10 @@ "message": "You can add notes for yourself in this conversation. If your account has any linked devices, new notes will be synced.", "description": "Description for the Note to Self conversation" }, + "notificationDrawAttention": { + "message": "Draw attention to this window when a notification arrives", + "description": "Label text for the setting that controls whether new notifications draw attention to the window" + }, "hideMenuBar": { "message": "Hide menu bar", "description": "Label text for menu bar visibility setting" diff --git a/js/background.js b/js/background.js index edb7daf5185..380b8dfc3da 100644 --- a/js/background.js +++ b/js/background.js @@ -373,6 +373,10 @@ storage.get('notification-setting', 'message'), setNotificationSetting: value => storage.put('notification-setting', value), + getNotificationDrawAttention: () => + storage.get('notification-draw-attention', true), + setNotificationDrawAttention: value => + storage.put('notification-draw-attention', value), getAudioNotification: () => storage.get('audio-notification'), setAudioNotification: value => storage.put('audio-notification', value), getCallRingtoneNotification: () => diff --git a/js/notifications.js b/js/notifications.js index 9664d8cdd1a..a63676886af 100644 --- a/js/notifications.js +++ b/js/notifications.js @@ -153,7 +153,13 @@ message = i18n('newMessage'); } - drawAttention(); + const shouldDrawAttention = storage.get( + 'notification-draw-attention', + true + ); + if (shouldDrawAttention) { + drawAttention(); + } this.lastNotification = window.Signal.Services.notify({ platform: window.platform, diff --git a/js/settings_start.js b/js/settings_start.js index 204b19b9bab..a945f3c2f15 100644 --- a/js/settings_start.js +++ b/js/settings_start.js @@ -36,6 +36,7 @@ const getInitialData = async () => ({ notificationSetting: await window.getNotificationSetting(), audioNotification: await window.getAudioNotification(), + notificationDrawAttention: await window.getNotificationDrawAttention(), spellCheck: await window.getSpellCheck(), diff --git a/js/views/settings_view.js b/js/views/settings_view.js index 56f7a3c4620..2a5a82ac041 100644 --- a/js/views/settings_view.js +++ b/js/views/settings_view.js @@ -113,6 +113,14 @@ window.setThemeSetting(theme); }, }); + if (Settings.isDrawAttentionSupported()) { + new CheckboxView({ + el: this.$('.draw-attention-setting'), + name: 'draw-attention-setting', + value: window.initialData.notificationDrawAttention, + setFn: window.setNotificationDrawAttention, + }); + } if (Settings.isAudioNotificationSupported()) { new CheckboxView({ el: this.$('.audio-notification-setting'), @@ -204,9 +212,11 @@ nameAndMessage: i18n('nameAndMessage'), noNameOrMessage: i18n('noNameOrMessage'), nameOnly: i18n('nameOnly'), + notificationDrawAttention: i18n('notificationDrawAttention'), audioNotificationDescription: i18n('audioNotificationDescription'), isAudioNotificationSupported: Settings.isAudioNotificationSupported(), isHideMenuBarSupported: Settings.isHideMenuBarSupported(), + isDrawAttentionSupported: Settings.isDrawAttentionSupported(), hasSystemTheme: true, themeLight: i18n('themeLight'), themeDark: i18n('themeDark'), diff --git a/main.js b/main.js index aa305c3428f..91eacdeb54a 100644 --- a/main.js +++ b/main.js @@ -64,10 +64,6 @@ const startInTray = process.argv.some(arg => arg === '--start-in-tray'); const usingTrayIcon = startInTray || process.argv.some(arg => arg === '--use-tray-icon'); -const disableFlashFrame = process.argv.some( - arg => arg === '--disable-flash-frame' -); - const config = require('./app/config'); // Very important to put before the single instance check, since it is based on the @@ -1165,9 +1161,6 @@ ipc.on('draw-attention', () => { if (!mainWindow) { return; } - if (disableFlashFrame) { - return; - } if (process.platform === 'win32' || process.platform === 'linux') { mainWindow.flashFrame(true); @@ -1262,6 +1255,8 @@ installSettingsSetter('hide-menu-bar'); installSettingsGetter('notification-setting'); installSettingsSetter('notification-setting'); +installSettingsGetter('notification-draw-attention'); +installSettingsSetter('notification-draw-attention'); installSettingsGetter('audio-notification'); installSettingsSetter('audio-notification'); diff --git a/preload.js b/preload.js index 1c7f3d7c1e8..0a491faef16 100644 --- a/preload.js +++ b/preload.js @@ -149,6 +149,8 @@ try { installGetter('notification-setting', 'getNotificationSetting'); installSetter('notification-setting', 'setNotificationSetting'); + installGetter('notification-draw-attention', 'getNotificationDrawAttention'); + installSetter('notification-draw-attention', 'setNotificationDrawAttention'); installGetter('audio-notification', 'getAudioNotification'); installSetter('audio-notification', 'setAudioNotification'); diff --git a/settings.html b/settings.html index a6956f45123..ac1dd03230a 100644 --- a/settings.html +++ b/settings.html @@ -88,6 +88,13 @@ + {{ #isDrawAttentionSupported }} +
+
+ + +
+ {{ /isDrawAttentionSupported }}
{{ #isAudioNotificationSupported }}
diff --git a/settings_preload.js b/settings_preload.js index 0aba2765274..919160c99e1 100644 --- a/settings_preload.js +++ b/settings_preload.js @@ -60,6 +60,8 @@ window.setAlwaysRelayCalls = makeSetter('always-relay-calls'); window.getNotificationSetting = makeGetter('notification-setting'); window.setNotificationSetting = makeSetter('notification-setting'); +window.getNotificationDrawAttention = makeGetter('notification-draw-attention'); +window.setNotificationDrawAttention = makeSetter('notification-draw-attention'); window.getAudioNotification = makeGetter('audio-notification'); window.setAudioNotification = makeSetter('audio-notification'); window.getCallRingtoneNotification = makeGetter('call-ringtone-notification'); diff --git a/ts/test/types/Settings_test.ts b/ts/test/types/Settings_test.ts index 714b1342ed3..3772b5abb3a 100644 --- a/ts/test/types/Settings_test.ts +++ b/ts/test/types/Settings_test.ts @@ -191,4 +191,65 @@ describe('Settings', () => { }); }); }); + describe('isDrawAttentionSupported', () => { + context('on macOS', () => { + beforeEach(() => { + sandbox.stub(process, 'platform').value('darwin'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return false', () => { + assert.isFalse(Settings.isDrawAttentionSupported()); + }); + }); + + context('on Windows', () => { + context('version 7', () => { + beforeEach(() => { + sandbox.stub(process, 'platform').value('win32'); + sandbox.stub(os, 'release').returns('7.0.0'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return true', () => { + assert.isTrue(Settings.isDrawAttentionSupported()); + }); + }); + + context('version 8+', () => { + beforeEach(() => { + sandbox.stub(process, 'platform').value('win32'); + sandbox.stub(os, 'release').returns('8.0.0'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return true', () => { + assert.isTrue(Settings.isDrawAttentionSupported()); + }); + }); + }); + + context('on Linux', () => { + beforeEach(() => { + sandbox.stub(process, 'platform').value('linux'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return true', () => { + assert.isTrue(Settings.isDrawAttentionSupported()); + }); + }); + }); }); diff --git a/ts/types/Settings.ts b/ts/types/Settings.ts index 5231bdf0fa8..0d6ca05d927 100644 --- a/ts/types/Settings.ts +++ b/ts/types/Settings.ts @@ -12,3 +12,6 @@ export const isNotificationGroupingSupported = () => // the "hide menu bar" option is specific to Windows and Linux export const isHideMenuBarSupported = () => !OS.isMacOS(); + +// the "draw attention on notification" option is specific to Windows and Linux +export const isDrawAttentionSupported = () => !OS.isMacOS(); diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index 4b0aeb64360..933baa258d1 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -324,9 +324,9 @@ "rule": "jQuery-appendTo(", "path": "js/settings_start.js", "line": " window.view.$el.appendTo($body);", - "lineNumber": 63, + "lineNumber": 64, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Interacting with already-existing DOM nodes" }, { @@ -911,141 +911,154 @@ { "rule": "jQuery-$(", "path": "js/views/settings_view.js", - "line": " el: this.$('.audio-notification-setting'),", + "line": " el: this.$('.draw-attention-setting'),", "lineNumber": 118, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", + "reasonDetail": "Protected from arbitrary input" + }, + { + "rule": "jQuery-$(", + "path": "js/views/settings_view.js", + "line": " el: this.$('.audio-notification-setting'),", + "lineNumber": 126, + "reasonCategory": "usageTrusted", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " el: this.$('.spell-check-setting'),", - "lineNumber": 125, + "lineNumber": 133, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " const $msg = this.$('.spell-check-setting-message');", - "lineNumber": 129, + "lineNumber": 137, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z" + "updated": "2020-08-21T11:29:29.636Z", + "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " el: this.$('.menu-bar-setting'),", - "lineNumber": 142, + "lineNumber": 150, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " el: this.$('.always-relay-calls-setting'),", - "lineNumber": 149, + "lineNumber": 157, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " el: this.$('.call-ringtone-notification-setting'),", - "lineNumber": 155, + "lineNumber": 163, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z" + "updated": "2020-08-21T11:29:29.636Z", + "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " el: this.$('.call-system-notification-setting'),", - "lineNumber": 161, + "lineNumber": 169, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z" + "updated": "2020-08-21T11:29:29.636Z", + "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " el: this.$('.incoming-call-notification-setting'),", - "lineNumber": 167, + "lineNumber": 175, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z" + "updated": "2020-08-21T11:29:29.636Z", + "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " el: this.$('.media-permissions'),", - "lineNumber": 173, + "lineNumber": 181, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " el: this.$('.media-camera-permissions'),", - "lineNumber": 178, + "lineNumber": 186, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " this.$('.sync-setting').append(syncView.el);", - "lineNumber": 184, + "lineNumber": 192, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-append(", "path": "js/views/settings_view.js", "line": " this.$('.sync-setting').append(syncView.el);", - "lineNumber": 184, + "lineNumber": 192, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Interacting with already-existing DOM nodes" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " this.$('.sync').text(i18n('syncNow'));", - "lineNumber": 263, + "lineNumber": 273, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " this.$('.sync').attr('disabled', 'disabled');", - "lineNumber": 267, + "lineNumber": 277, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " this.$('.synced_at').hide();", - "lineNumber": 279, + "lineNumber": 289, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Protected from arbitrary input" }, { "rule": "jQuery-$(", "path": "js/views/settings_view.js", "line": " this.$('.sync_failed').hide();", - "lineNumber": 284, + "lineNumber": 294, "reasonCategory": "usageTrusted", - "updated": "2020-06-02T21:51:34.813Z", + "updated": "2020-08-21T11:29:29.636Z", "reasonDetail": "Protected from arbitrary input" }, {