Skip to content

Commit

Permalink
Add setting to toggle notification attention drawing (#4457)
Browse files Browse the repository at this point in the history
In many GNU/Linux setups, drawing attention when a notification arrives
causes the Signal window to steal focus immediately and interrupt the
user from what they were doing before the notification arrived. GNOME
Shell is the most prominent example of this behavior, but there are
likely other cases as well. Suddenly stealing focus on external events
like this can even pose a security problem in some cases, e.g. if the
user is in the middle of a typing a sudo password on one monitor while a
notification arrives and focuses Signal on another monitor. See #4452
for more information.

Disabling attention drawing entirely for Linux is also problematic
because some users rely on it as the sole indication of a new message,
as seen in #3582 and #3611.

Commit f790694 improved the situation
by adding a hidden "--disable-flash-frame" command-line argument, but
this argument is undocumented and manually adding command-line arguments
to the application's .desktop file is not user-friendly.

This commit adds a settings option for whether to draw attention when a
new notification arrives to make it easy for all Linux users to obtain
the appropriate behavior without relying on an undocumented
command-line argument.

Fixes #4452.
  • Loading branch information
kdrag0n authored Aug 24, 2020
1 parent 0c9e7ce commit c826181
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 42 deletions.
4 changes: 4 additions & 0 deletions _locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions js/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: () =>
Expand Down
8 changes: 7 additions & 1 deletion js/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions js/settings_start.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const getInitialData = async () => ({

notificationSetting: await window.getNotificationSetting(),
audioNotification: await window.getAudioNotification(),
notificationDrawAttention: await window.getNotificationDrawAttention(),

spellCheck: await window.getSpellCheck(),

Expand Down
10 changes: 10 additions & 0 deletions js/views/settings_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down Expand Up @@ -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'),
Expand Down
9 changes: 2 additions & 7 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1165,9 +1161,6 @@ ipc.on('draw-attention', () => {
if (!mainWindow) {
return;
}
if (disableFlashFrame) {
return;
}

if (process.platform === 'win32' || process.platform === 'linux') {
mainWindow.flashFrame(true);
Expand Down Expand Up @@ -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');

Expand Down
2 changes: 2 additions & 0 deletions preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down
7 changes: 7 additions & 0 deletions settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ <h3>{{ notifications }}</h3>
<label for='notification-setting-off'>{{ disableNotifications }} </label>
</div>
</div>
{{ #isDrawAttentionSupported }}
<br />
<div class='draw-attention-setting'>
<input type='checkbox' name='notification-draw-attention' id='notification-draw-attention'/>
<label for='notification-draw-attention'>{{ notificationDrawAttention }}</label>
</div>
{{ /isDrawAttentionSupported }}
<br />
{{ #isAudioNotificationSupported }}
<div class='audio-notification-setting'>
Expand Down
2 changes: 2 additions & 0 deletions settings_preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
61 changes: 61 additions & 0 deletions ts/test/types/Settings_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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());
});
});
});
});
3 changes: 3 additions & 0 deletions ts/types/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Loading

0 comments on commit c826181

Please sign in to comment.