Add sound effect for notifications in Linux

This commit is contained in:
Evan Hahn 2020-09-11 16:23:11 -05:00 committed by Josh Perez
parent 6d2e994f9f
commit de66486e41
7 changed files with 146 additions and 234 deletions

View file

@ -10,7 +10,6 @@
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function() { (function() {
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
const { Settings } = Signal.Types;
// The keys and values don't match here. This is because the values correspond to old // The keys and values don't match here. This is because the values correspond to old
// setting names. In the future, we may wish to migrate these to match. // setting names. In the future, we may wish to migrate these to match.
@ -76,13 +75,11 @@
const isAppFocused = window.isActive(); const isAppFocused = window.isActive();
const isAudioNotificationEnabled = const isAudioNotificationEnabled =
storage.get('audio-notification') || false; storage.get('audio-notification') || false;
const isAudioNotificationSupported = Settings.isAudioNotificationSupported();
const userSetting = this.getUserSetting(); const userSetting = this.getUserSetting();
const status = Signal.Notifications.getStatus({ const status = Signal.Notifications.getStatus({
isAppFocused, isAppFocused,
isAudioNotificationEnabled, isAudioNotificationEnabled,
isAudioNotificationSupported,
isEnabled, isEnabled,
hasNotifications: Boolean(this.notificationData), hasNotifications: Boolean(this.notificationData),
userSetting, userSetting,
@ -157,7 +154,6 @@
} }
this.lastNotification = window.Signal.Services.notify({ this.lastNotification = window.Signal.Services.notify({
platform: window.platform,
title: notificationTitle, title: notificationTitle,
icon: notificationIconUrl, icon: notificationIconUrl,
message: notificationMessage, message: notificationMessage,

BIN
sounds/notification.ogg Executable file

Binary file not shown.

View file

@ -1,7 +1,6 @@
interface Environment { interface Environment {
isAppFocused: boolean; isAppFocused: boolean;
isAudioNotificationEnabled: boolean; isAudioNotificationEnabled: boolean;
isAudioNotificationSupported: boolean;
isEnabled: boolean; isEnabled: boolean;
hasNotifications: boolean; hasNotifications: boolean;
userSetting: UserSetting; userSetting: UserSetting;
@ -26,7 +25,6 @@ type Type =
export const getStatus = ({ export const getStatus = ({
isAppFocused, isAppFocused,
isAudioNotificationEnabled, isAudioNotificationEnabled,
isAudioNotificationSupported,
isEnabled, isEnabled,
hasNotifications, hasNotifications,
userSetting, userSetting,
@ -51,15 +49,10 @@ export const getStatus = ({
return 'ok'; return 'ok';
})(); })();
const shouldPlayNotificationSound =
isAudioNotificationSupported && isAudioNotificationEnabled;
const shouldShowNotifications = type === 'ok';
const shouldClearNotifications = type === 'appIsFocused';
return { return {
shouldClearNotifications, shouldClearNotifications: type === 'appIsFocused',
shouldPlayNotificationSound, shouldPlayNotificationSound: isAudioNotificationEnabled,
shouldShowNotifications, shouldShowNotifications: type === 'ok',
type, type,
}; };
}; };

View file

@ -1,3 +1,10 @@
import { Sound } from '../util/Sound';
import {
AudioNotificationSupport,
getAudioNotificationSupport,
} from '../types/Settings';
import * as OS from '../OS';
function filter(text: string) { function filter(text: string) {
return (text || '') return (text || '')
.replace(/&/g, '&') .replace(/&/g, '&')
@ -8,7 +15,6 @@ function filter(text: string) {
} }
type NotificationType = { type NotificationType = {
platform: string;
icon: string; icon: string;
message: string; message: string;
onNotificationClick: () => void; onNotificationClick: () => void;
@ -17,18 +23,27 @@ type NotificationType = {
}; };
export function notify({ export function notify({
platform,
icon, icon,
message, message,
onNotificationClick, onNotificationClick,
silent, silent,
title, title,
}: NotificationType): Notification { }: NotificationType): Notification {
const audioNotificationSupport = getAudioNotificationSupport();
const notification = new window.Notification(title, { const notification = new window.Notification(title, {
body: platform === 'linux' ? filter(message) : message, body: OS.isLinux() ? filter(message) : message,
icon, icon,
silent, silent:
silent || audioNotificationSupport !== AudioNotificationSupport.Native,
}); });
notification.onclick = onNotificationClick; notification.onclick = onNotificationClick;
if (!silent && audioNotificationSupport === AudioNotificationSupport.Custom) {
// We kick off the sound to be played. No neet to await it.
// tslint:disable-next-line no-floating-promises
new Sound({ src: 'sounds/notification.ogg' }).play();
}
return notification; return notification;
} }

View file

@ -277,7 +277,6 @@ async function showCallNotification(callDetails: CallDetailsType) {
} }
const { title, isVideoCall } = callDetails; const { title, isVideoCall } = callDetails;
notify({ notify({
platform: window.platform,
title, title,
icon: isVideoCall icon: isVideoCall
? 'images/icons/v2/video-solid-24.svg' ? 'images/icons/v2/video-solid-24.svg'

View file

@ -5,251 +5,145 @@ import { assert } from 'chai';
import * as Settings from '../../../ts/types/Settings'; import * as Settings from '../../../ts/types/Settings';
describe('Settings', () => { describe('Settings', () => {
const sandbox = Sinon.createSandbox(); let sandbox: Sinon.SinonSandbox;
beforeEach(() => {
sandbox = Sinon.createSandbox();
});
afterEach(() => {
sandbox.restore();
});
describe('getAudioNotificationSupport', () => {
it('returns native support on macOS', () => {
sandbox.stub(process, 'platform').value('darwin');
assert.strictEqual(
Settings.getAudioNotificationSupport(),
Settings.AudioNotificationSupport.Native
);
});
it('returns no support on Windows 7', () => {
sandbox.stub(process, 'platform').value('win32');
sandbox.stub(os, 'release').returns('7.0.0');
assert.strictEqual(
Settings.getAudioNotificationSupport(),
Settings.AudioNotificationSupport.None
);
});
it('returns native support on Windows 8', () => {
sandbox.stub(process, 'platform').value('win32');
sandbox.stub(os, 'release').returns('8.0.0');
assert.strictEqual(
Settings.getAudioNotificationSupport(),
Settings.AudioNotificationSupport.Native
);
});
it('returns custom support on Linux', () => {
sandbox.stub(process, 'platform').value('linux');
assert.strictEqual(
Settings.getAudioNotificationSupport(),
Settings.AudioNotificationSupport.Custom
);
});
});
describe('isAudioNotificationSupported', () => { describe('isAudioNotificationSupported', () => {
context('on macOS', () => { it('returns true on macOS', () => {
beforeEach(() => { sandbox.stub(process, 'platform').value('darwin');
sandbox.stub(process, 'platform').value('darwin'); assert.isTrue(Settings.isAudioNotificationSupported());
});
afterEach(() => {
sandbox.restore();
});
it('should return true', () => {
assert.isTrue(Settings.isAudioNotificationSupported());
});
}); });
context('on Windows', () => { it('returns false on Windows 7', () => {
context('version 7', () => { sandbox.stub(process, 'platform').value('win32');
beforeEach(() => { sandbox.stub(os, 'release').returns('7.0.0');
sandbox.stub(process, 'platform').value('win32'); assert.isFalse(Settings.isAudioNotificationSupported());
sandbox.stub(os, 'release').returns('7.0.0');
});
afterEach(() => {
sandbox.restore();
});
it('should return false', () => {
assert.isFalse(Settings.isAudioNotificationSupported());
});
});
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.isAudioNotificationSupported());
});
});
}); });
context('on Linux', () => { it('returns true on Windows 8', () => {
beforeEach(() => { sandbox.stub(process, 'platform').value('win32');
sandbox.stub(process, 'platform').value('linux'); sandbox.stub(os, 'release').returns('8.0.0');
}); assert.isTrue(Settings.isAudioNotificationSupported());
});
afterEach(() => { it('returns true on Linux', () => {
sandbox.restore(); sandbox.stub(process, 'platform').value('linux');
}); assert.isTrue(Settings.isAudioNotificationSupported());
it('should return false', () => {
assert.isFalse(Settings.isAudioNotificationSupported());
});
}); });
}); });
describe('isNotificationGroupingSupported', () => { describe('isNotificationGroupingSupported', () => {
context('on macOS', () => { it('returns true on macOS', () => {
beforeEach(() => { sandbox.stub(process, 'platform').value('darwin');
sandbox.stub(process, 'platform').value('darwin'); assert.isTrue(Settings.isNotificationGroupingSupported());
});
afterEach(() => {
sandbox.restore();
});
it('should return true', () => {
assert.isTrue(Settings.isNotificationGroupingSupported());
});
}); });
context('on Windows', () => { it('returns true on Windows 7', () => {
context('version 7', () => { sandbox.stub(process, 'platform').value('win32');
beforeEach(() => { sandbox.stub(os, 'release').returns('7.0.0');
sandbox.stub(process, 'platform').value('win32'); assert.isFalse(Settings.isNotificationGroupingSupported());
sandbox.stub(os, 'release').returns('7.0.0');
});
afterEach(() => {
sandbox.restore();
});
it('should return false', () => {
assert.isFalse(Settings.isNotificationGroupingSupported());
});
});
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.isNotificationGroupingSupported());
});
});
}); });
context('on Linux', () => { it('returns true on Windows 8', () => {
beforeEach(() => { sandbox.stub(process, 'platform').value('win32');
sandbox.stub(process, 'platform').value('linux'); sandbox.stub(os, 'release').returns('8.0.0');
}); assert.isTrue(Settings.isNotificationGroupingSupported());
});
afterEach(() => { it('returns true on Linux', () => {
sandbox.restore(); sandbox.stub(process, 'platform').value('linux');
}); assert.isTrue(Settings.isNotificationGroupingSupported());
it('should return true', () => {
assert.isTrue(Settings.isNotificationGroupingSupported());
});
}); });
}); });
describe('isHideMenuBarSupported', () => { describe('isHideMenuBarSupported', () => {
context('on macOS', () => { it('returns false on macOS', () => {
beforeEach(() => { sandbox.stub(process, 'platform').value('darwin');
sandbox.stub(process, 'platform').value('darwin'); assert.isFalse(Settings.isHideMenuBarSupported());
});
afterEach(() => {
sandbox.restore();
});
it('should return false', () => {
assert.isFalse(Settings.isHideMenuBarSupported());
});
}); });
context('on Windows', () => { it('returns true on Windows 7', () => {
context('version 7', () => { sandbox.stub(process, 'platform').value('win32');
beforeEach(() => { sandbox.stub(os, 'release').returns('7.0.0');
sandbox.stub(process, 'platform').value('win32'); assert.isTrue(Settings.isHideMenuBarSupported());
sandbox.stub(os, 'release').returns('7.0.0');
});
afterEach(() => {
sandbox.restore();
});
it('should return true', () => {
assert.isTrue(Settings.isHideMenuBarSupported());
});
});
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.isHideMenuBarSupported());
});
});
}); });
context('on Linux', () => { it('returns true on Windows 8', () => {
beforeEach(() => { sandbox.stub(process, 'platform').value('win32');
sandbox.stub(process, 'platform').value('linux'); sandbox.stub(os, 'release').returns('8.0.0');
}); assert.isTrue(Settings.isHideMenuBarSupported());
});
afterEach(() => { it('returns true on Linux', () => {
sandbox.restore(); sandbox.stub(process, 'platform').value('linux');
}); assert.isTrue(Settings.isHideMenuBarSupported());
it('should return true', () => {
assert.isTrue(Settings.isHideMenuBarSupported());
});
}); });
}); });
describe('isDrawAttentionSupported', () => { describe('isDrawAttentionSupported', () => {
context('on macOS', () => { it('returns false on macOS', () => {
beforeEach(() => { sandbox.stub(process, 'platform').value('darwin');
sandbox.stub(process, 'platform').value('darwin'); assert.isFalse(Settings.isDrawAttentionSupported());
});
afterEach(() => {
sandbox.restore();
});
it('should return false', () => {
assert.isFalse(Settings.isDrawAttentionSupported());
});
}); });
context('on Windows', () => { it('returns true on Windows 7', () => {
context('version 7', () => { sandbox.stub(process, 'platform').value('win32');
beforeEach(() => { sandbox.stub(os, 'release').returns('7.0.0');
sandbox.stub(process, 'platform').value('win32'); assert.isTrue(Settings.isDrawAttentionSupported());
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', () => { it('returns true on Windows 8', () => {
beforeEach(() => { sandbox.stub(process, 'platform').value('win32');
sandbox.stub(process, 'platform').value('linux'); sandbox.stub(os, 'release').returns('8.0.0');
}); assert.isTrue(Settings.isDrawAttentionSupported());
});
afterEach(() => { it('returns true on Linux', () => {
sandbox.restore(); sandbox.stub(process, 'platform').value('linux');
}); assert.isTrue(Settings.isDrawAttentionSupported());
it('should return true', () => {
assert.isTrue(Settings.isDrawAttentionSupported());
});
}); });
}); });
}); });

View file

@ -2,8 +2,23 @@ import * as OS from '../OS';
const MIN_WINDOWS_VERSION = '8.0.0'; const MIN_WINDOWS_VERSION = '8.0.0';
export const isAudioNotificationSupported = () => export enum AudioNotificationSupport {
OS.isWindows(MIN_WINDOWS_VERSION) || OS.isMacOS(); None,
Native,
Custom,
}
export function getAudioNotificationSupport(): AudioNotificationSupport {
if (OS.isWindows(MIN_WINDOWS_VERSION) || OS.isMacOS()) {
return AudioNotificationSupport.Native;
} else if (OS.isLinux()) {
return AudioNotificationSupport.Custom;
}
return AudioNotificationSupport.None;
}
export const isAudioNotificationSupported = (): boolean =>
getAudioNotificationSupport() !== AudioNotificationSupport.None;
// Using `Notification::tag` has a bug on Windows 7: // Using `Notification::tag` has a bug on Windows 7:
// https://github.com/electron/electron/issues/11189 // https://github.com/electron/electron/issues/11189