Adds a pop and whoosh sound for message receive/sent

This commit is contained in:
Josh Perez 2023-05-08 15:59:36 -04:00
parent c7a430f375
commit 272b81c7cf
20 changed files with 141 additions and 145 deletions

View file

@ -10434,6 +10434,14 @@
"messageformat": "Group Avatar", "messageformat": "Group Avatar",
"description": "Title for the avatar picker in the group creation flow" "description": "Title for the avatar picker in the group creation flow"
}, },
"icu:Preferences__message-audio-title": {
"messageformat": "In-chat message sound",
"description": "Title for message audio setting"
},
"icu:Preferences__message-audio-description": {
"messageformat": "Hear a notification sound for sent and received messages while in the chat.",
"description": "Description for message audio setting"
},
"Preferences__button--general": { "Preferences__button--general": {
"message": "General", "message": "General",
"description": "(deleted 03/29/2023) Button to switch the settings view" "description": "(deleted 03/29/2023) Button to switch the settings view"

Binary file not shown.

BIN
sounds/pop.wav Normal file

Binary file not shown.

BIN
sounds/whoosh.wav Normal file

Binary file not shown.

View file

@ -81,6 +81,7 @@ const getDefaultArgs = (): PropsDataType => ({
hasLinkPreviews: true, hasLinkPreviews: true,
hasMediaCameraPermissions: true, hasMediaCameraPermissions: true,
hasMediaPermissions: true, hasMediaPermissions: true,
hasMessageAudio: true,
hasMinimizeToAndStartInSystemTray: true, hasMinimizeToAndStartInSystemTray: true,
hasMinimizeToSystemTray: true, hasMinimizeToSystemTray: true,
hasNotificationAttention: false, hasNotificationAttention: false,
@ -92,7 +93,6 @@ const getDefaultArgs = (): PropsDataType => ({
hasTextFormatting: true, hasTextFormatting: true,
hasTypingIndicators: true, hasTypingIndicators: true,
initialSpellCheckSetting: true, initialSpellCheckSetting: true,
isAudioNotificationsSupported: true,
isAutoDownloadUpdatesSupported: true, isAutoDownloadUpdatesSupported: true,
isAutoLaunchSupported: true, isAutoLaunchSupported: true,
isFormattingFlagEnabled: true, isFormattingFlagEnabled: true,
@ -152,6 +152,7 @@ export default {
onLastSyncTimeChange: { action: true }, onLastSyncTimeChange: { action: true },
onMediaCameraPermissionsChange: { action: true }, onMediaCameraPermissionsChange: { action: true },
onMediaPermissionsChange: { action: true }, onMediaPermissionsChange: { action: true },
onMessageAudioChange: { action: true },
onMinimizeToAndStartInSystemTrayChange: { action: true }, onMinimizeToAndStartInSystemTrayChange: { action: true },
onMinimizeToSystemTrayChange: { action: true }, onMinimizeToSystemTrayChange: { action: true },
onNotificationAttentionChange: { action: true }, onNotificationAttentionChange: { action: true },

View file

@ -80,6 +80,7 @@ export type PropsDataType = {
hasLinkPreviews: boolean; hasLinkPreviews: boolean;
hasMediaCameraPermissions: boolean; hasMediaCameraPermissions: boolean;
hasMediaPermissions: boolean; hasMediaPermissions: boolean;
hasMessageAudio: boolean;
hasMinimizeToAndStartInSystemTray: boolean; hasMinimizeToAndStartInSystemTray: boolean;
hasMinimizeToSystemTray: boolean; hasMinimizeToSystemTray: boolean;
hasNotificationAttention: boolean; hasNotificationAttention: boolean;
@ -111,7 +112,6 @@ export type PropsDataType = {
isFormattingFlagEnabled: boolean; isFormattingFlagEnabled: boolean;
// Limited support features // Limited support features
isAudioNotificationsSupported: boolean;
isAutoDownloadUpdatesSupported: boolean; isAutoDownloadUpdatesSupported: boolean;
isAutoLaunchSupported: boolean; isAutoLaunchSupported: boolean;
isHideMenuBarSupported: boolean; isHideMenuBarSupported: boolean;
@ -163,6 +163,7 @@ type PropsFunctionType = {
onLastSyncTimeChange: (time: number) => unknown; onLastSyncTimeChange: (time: number) => unknown;
onMediaCameraPermissionsChange: CheckboxChangeHandlerType; onMediaCameraPermissionsChange: CheckboxChangeHandlerType;
onMediaPermissionsChange: CheckboxChangeHandlerType; onMediaPermissionsChange: CheckboxChangeHandlerType;
onMessageAudioChange: CheckboxChangeHandlerType;
onMinimizeToAndStartInSystemTrayChange: CheckboxChangeHandlerType; onMinimizeToAndStartInSystemTrayChange: CheckboxChangeHandlerType;
onMinimizeToSystemTrayChange: CheckboxChangeHandlerType; onMinimizeToSystemTrayChange: CheckboxChangeHandlerType;
onNotificationAttentionChange: CheckboxChangeHandlerType; onNotificationAttentionChange: CheckboxChangeHandlerType;
@ -252,6 +253,7 @@ export function Preferences({
hasLinkPreviews, hasLinkPreviews,
hasMediaCameraPermissions, hasMediaCameraPermissions,
hasMediaPermissions, hasMediaPermissions,
hasMessageAudio,
hasMinimizeToAndStartInSystemTray, hasMinimizeToAndStartInSystemTray,
hasMinimizeToSystemTray, hasMinimizeToSystemTray,
hasNotificationAttention, hasNotificationAttention,
@ -264,7 +266,6 @@ export function Preferences({
hasTypingIndicators, hasTypingIndicators,
i18n, i18n,
initialSpellCheckSetting, initialSpellCheckSetting,
isAudioNotificationsSupported,
isAutoDownloadUpdatesSupported, isAutoDownloadUpdatesSupported,
isAutoLaunchSupported, isAutoLaunchSupported,
isFormattingFlagEnabled, isFormattingFlagEnabled,
@ -290,6 +291,7 @@ export function Preferences({
onLastSyncTimeChange, onLastSyncTimeChange,
onMediaCameraPermissionsChange, onMediaCameraPermissionsChange,
onMediaPermissionsChange, onMediaPermissionsChange,
onMessageAudioChange,
onMinimizeToAndStartInSystemTrayChange, onMinimizeToAndStartInSystemTrayChange,
onMinimizeToSystemTrayChange, onMinimizeToSystemTrayChange,
onNotificationAttentionChange, onNotificationAttentionChange,
@ -857,15 +859,6 @@ export function Preferences({
onChange={onNotificationAttentionChange} onChange={onNotificationAttentionChange}
/> />
)} )}
{isAudioNotificationsSupported && (
<Checkbox
checked={hasAudioNotifications}
label={i18n('icu:audioNotificationDescription')}
moduleClassName="Preferences__checkbox"
name="audioNotification"
onChange={onAudioNotificationsChange}
/>
)}
<Checkbox <Checkbox
checked={hasCountMutedConversations} checked={hasCountMutedConversations}
label={i18n('icu:countMutedConversationsDescription')} label={i18n('icu:countMutedConversationsDescription')}
@ -901,6 +894,24 @@ export function Preferences({
} }
/> />
</SettingsRow> </SettingsRow>
<SettingsRow>
<Checkbox
checked={hasAudioNotifications}
label={i18n('icu:audioNotificationDescription')}
moduleClassName="Preferences__checkbox"
name="audioNotification"
onChange={onAudioNotificationsChange}
/>
<Checkbox
checked={hasMessageAudio}
description={i18n('icu:Preferences__message-audio-description')}
disabled={!hasAudioNotifications}
label={i18n('icu:Preferences__message-audio-title')}
moduleClassName="Preferences__checkbox"
name="messageAudio"
onChange={onMessageAudioChange}
/>
</SettingsRow>
</> </>
); );
} else if (page === Page.Privacy) { } else if (page === Page.Privacy) {

View file

@ -81,6 +81,7 @@ export class SettingsChannel extends EventEmitter {
this.installSetting('notificationSetting'); this.installSetting('notificationSetting');
this.installSetting('notificationDrawAttention'); this.installSetting('notificationDrawAttention');
this.installSetting('audioMessage');
this.installSetting('audioNotification'); this.installSetting('audioNotification');
this.installSetting('countMutedConversations'); this.installSetting('countMutedConversations');

View file

@ -4,32 +4,30 @@
import os from 'os'; import os from 'os';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import EventEmitter from 'events'; import EventEmitter from 'events';
import { Sound } from '../util/Sound'; import { Sound, SoundType } from '../util/Sound';
import { import { shouldHideExpiringMessageBody } from '../types/Settings';
AudioNotificationSupport,
getAudioNotificationSupport,
shouldHideExpiringMessageBody,
} from '../types/Settings';
import OS from '../util/os/osMain'; import OS from '../util/os/osMain';
import * as log from '../logging/log'; import * as log from '../logging/log';
import { makeEnumParser } from '../util/enum'; import { makeEnumParser } from '../util/enum';
import { missingCaseError } from '../util/missingCaseError'; import { missingCaseError } from '../util/missingCaseError';
import type { StorageInterface } from '../types/Storage.d'; import type { StorageInterface } from '../types/Storage.d';
import type { LocalizerType } from '../types/Util'; import type { LocalizerType } from '../types/Util';
import { drop } from '../util/drop';
type NotificationDataType = Readonly<{ type NotificationDataType = Readonly<{
conversationId: string; conversationId: string;
storyId?: string; isExpiringMessage: boolean;
messageId: string; messageId: string;
senderTitle: string;
message: string; message: string;
notificationIconUrl?: undefined | string; notificationIconUrl?: undefined | string;
isExpiringMessage: boolean;
reaction?: { reaction?: {
emoji: string; emoji: string;
targetAuthorUuid: string; targetAuthorUuid: string;
targetTimestamp: number; targetTimestamp: number;
}; };
senderTitle: string;
storyId?: string;
useTriToneSound?: boolean;
wasShown?: boolean; wasShown?: boolean;
}>; }>;
@ -133,6 +131,7 @@ class NotificationService extends EventEmitter {
onNotificationClick, onNotificationClick,
silent, silent,
title, title,
useTriToneSound,
}: Readonly<{ }: Readonly<{
icon?: string; icon?: string;
message: string; message: string;
@ -140,28 +139,25 @@ class NotificationService extends EventEmitter {
onNotificationClick: () => void; onNotificationClick: () => void;
silent: boolean; silent: boolean;
title: string; title: string;
useTriToneSound?: boolean;
}>): void { }>): void {
log.info('NotificationService: showing a notification'); log.info('NotificationService: showing a notification');
this.lastNotification?.close(); this.lastNotification?.close();
const audioNotificationSupport = getAudioNotificationSupport(OS);
const notification = new window.Notification(title, { const notification = new window.Notification(title, {
body: OS.isLinux() ? filterNotificationText(message) : message, body: OS.isLinux() ? filterNotificationText(message) : message,
icon, icon,
silent: silent: true,
silent || audioNotificationSupport !== AudioNotificationSupport.Native,
tag: messageId, tag: messageId,
}); });
notification.onclick = onNotificationClick; notification.onclick = onNotificationClick;
if ( if (!silent) {
!silent && const soundType =
audioNotificationSupport === AudioNotificationSupport.Custom messageId && !useTriToneSound ? SoundType.Pop : SoundType.TriTone;
) {
// We kick off the sound to be played. No need to await it. // We kick off the sound to be played. No need to await it.
void new Sound({ src: 'sounds/notification.ogg' }).play(); drop(new Sound({ soundType }).play());
} }
this.lastNotification = notification; this.lastNotification = notification;
@ -273,12 +269,13 @@ class NotificationService extends EventEmitter {
const { const {
conversationId, conversationId,
storyId,
messageId,
senderTitle,
message,
isExpiringMessage, isExpiringMessage,
message,
messageId,
reaction, reaction,
senderTitle,
storyId,
useTriToneSound,
wasShown, wasShown,
} = notificationData; } = notificationData;
@ -346,13 +343,15 @@ class NotificationService extends EventEmitter {
}; };
this.notify({ this.notify({
title: notificationTitle,
icon: notificationIconUrl, icon: notificationIconUrl,
messageId,
message: notificationMessage, message: notificationMessage,
silent: !shouldPlayNotificationSound,
onNotificationClick: () => { onNotificationClick: () => {
this.emit('click', conversationId, messageId, storyId); this.emit('click', conversationId, messageId, storyId);
}, },
silent: !shouldPlayNotificationSound,
title: notificationTitle,
useTriToneSound,
}); });
} }

View file

@ -89,6 +89,7 @@ import { strictAssert } from '../../util/assert';
import { makeQuote } from '../../util/makeQuote'; import { makeQuote } from '../../util/makeQuote';
import { sendEditedMessage as doSendEditedMessage } from '../../util/sendEditedMessage'; import { sendEditedMessage as doSendEditedMessage } from '../../util/sendEditedMessage';
import { maybeBlockSendForFormattingModal } from '../../util/maybeBlockSendForFormattingModal'; import { maybeBlockSendForFormattingModal } from '../../util/maybeBlockSendForFormattingModal';
import { Sound, SoundType } from '../../util/Sound';
// State // State
// eslint-disable-next-line local-rules/type-alias-readonlydeep // eslint-disable-next-line local-rules/type-alias-readonlydeep
@ -616,6 +617,8 @@ function sendMultiMediaMessage(
); );
dispatch(incrementSendCounter(conversationId)); dispatch(incrementSendCounter(conversationId));
dispatch(setComposerDisabledState(conversationId, false)); dispatch(setComposerDisabledState(conversationId, false));
drop(new Sound({ soundType: SoundType.Whoosh }).play());
}, },
} }
); );

View file

@ -89,7 +89,8 @@ async function notifyForCall(
onNotificationClick: () => { onNotificationClick: () => {
window.IPC.showWindow(); window.IPC.showWindow();
}, },
silent: false, // The ringtone plays so we don't need sound for the notification
silent: true,
}); });
} }

View file

@ -13,7 +13,7 @@ import {
import { globalMessageAudio } from '../../services/globalMessageAudio'; import { globalMessageAudio } from '../../services/globalMessageAudio';
import { strictAssert } from '../../util/assert'; import { strictAssert } from '../../util/assert';
import * as log from '../../logging/log'; import * as log from '../../logging/log';
import { Sound } from '../../util/Sound'; import { Sound, SoundType } from '../../util/Sound';
import { getConversations } from '../selectors/conversations'; import { getConversations } from '../selectors/conversations';
import { SeenStatus } from '../../MessageSeenStatus'; import { SeenStatus } from '../../MessageSeenStatus';
import { markViewed } from '../ducks/conversations'; import { markViewed } from '../ducks/conversations';
@ -21,10 +21,10 @@ import * as Errors from '../../types/errors';
import { usePrevious } from '../../hooks/usePrevious'; import { usePrevious } from '../../hooks/usePrevious';
const stateChangeConfirmUpSound = new Sound({ const stateChangeConfirmUpSound = new Sound({
src: 'sounds/state-change_confirm-up.ogg', soundType: SoundType.VoiceNoteStart,
}); });
const stateChangeConfirmDownSound = new Sound({ const stateChangeConfirmDownSound = new Sound({
src: 'sounds/state-change_confirm-down.ogg', soundType: SoundType.VoiceNoteEnd,
}); });
/** /**

View file

@ -19,74 +19,6 @@ describe('Settings', () => {
sandbox.restore(); sandbox.restore();
}); });
describe('getAudioNotificationSupport', () => {
it('returns native support on macOS', () => {
sandbox.stub(process, 'platform').value('darwin');
const OS = getOSFunctions(os.release());
assert.strictEqual(
Settings.getAudioNotificationSupport(OS),
Settings.AudioNotificationSupport.Native
);
});
it('returns no support on Windows 7', () => {
sandbox.stub(process, 'platform').value('win32');
sandbox.stub(os, 'release').returns('7.0.0');
const OS = getOSFunctions(os.release());
assert.strictEqual(
Settings.getAudioNotificationSupport(OS),
Settings.AudioNotificationSupport.None
);
});
it('returns native support on Windows 8', () => {
sandbox.stub(process, 'platform').value('win32');
sandbox.stub(os, 'release').returns('8.0.0');
const OS = getOSFunctions(os.release());
assert.strictEqual(
Settings.getAudioNotificationSupport(OS),
Settings.AudioNotificationSupport.Native
);
});
it('returns custom support on Linux', () => {
sandbox.stub(process, 'platform').value('linux');
const OS = getOSFunctions(os.release());
assert.strictEqual(
Settings.getAudioNotificationSupport(OS),
Settings.AudioNotificationSupport.Custom
);
});
});
describe('isAudioNotificationSupported', () => {
it('returns true on macOS', () => {
sandbox.stub(process, 'platform').value('darwin');
const OS = getOSFunctions(os.release());
assert.isTrue(Settings.isAudioNotificationSupported(OS));
});
it('returns false on Windows 7', () => {
sandbox.stub(process, 'platform').value('win32');
sandbox.stub(os, 'release').returns('7.0.0');
const OS = getOSFunctions(os.release());
assert.isFalse(Settings.isAudioNotificationSupported(OS));
});
it('returns true on Windows 8', () => {
sandbox.stub(process, 'platform').value('win32');
sandbox.stub(os, 'release').returns('8.0.0');
const OS = getOSFunctions(os.release());
assert.isTrue(Settings.isAudioNotificationSupported(OS));
});
it('returns true on Linux', () => {
sandbox.stub(process, 'platform').value('linux');
const OS = getOSFunctions(os.release());
assert.isTrue(Settings.isAudioNotificationSupported(OS));
});
});
describe('isNotificationGroupingSupported', () => { describe('isNotificationGroupingSupported', () => {
it('returns true on macOS', () => { it('returns true on macOS', () => {
sandbox.stub(process, 'platform').value('darwin'); sandbox.stub(process, 'platform').value('darwin');

View file

@ -8,27 +8,6 @@ import { isProduction } from '../util/version';
const MIN_WINDOWS_VERSION = '8.0.0'; const MIN_WINDOWS_VERSION = '8.0.0';
export enum AudioNotificationSupport {
None,
Native,
Custom,
}
export function getAudioNotificationSupport(
OS: OSType
): AudioNotificationSupport {
if (OS.isWindows(MIN_WINDOWS_VERSION) || OS.isMacOS()) {
return AudioNotificationSupport.Native;
}
if (OS.isLinux()) {
return AudioNotificationSupport.Custom;
}
return AudioNotificationSupport.None;
}
export const isAudioNotificationSupported = (OS: OSType): boolean =>
getAudioNotificationSupport(OS) !== 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
export const isNotificationGroupingSupported = (OS: OSType): boolean => export const isNotificationGroupingSupported = (OS: OSType): boolean =>

View file

@ -62,6 +62,7 @@ export type StorageAccessType = {
'spell-check': boolean; 'spell-check': boolean;
'system-tray-setting': SystemTraySetting; 'system-tray-setting': SystemTraySetting;
'theme-setting': ThemeSettingType; 'theme-setting': ThemeSettingType;
audioMessage: boolean;
attachmentMigration_isComplete: boolean; attachmentMigration_isComplete: boolean;
attachmentMigration_lastProcessedIndex: number; attachmentMigration_lastProcessedIndex: number;
blocked: ReadonlyArray<string>; blocked: ReadonlyArray<string>;

View file

@ -2,14 +2,26 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import * as log from '../logging/log'; import * as log from '../logging/log';
import { missingCaseError } from './missingCaseError';
export enum SoundType {
CallingHangUp,
CallingPresenting,
Pop,
Ringtone,
TriTone,
VoiceNoteEnd,
VoiceNoteStart,
Whoosh,
}
export type SoundOpts = { export type SoundOpts = {
loop?: boolean; loop?: boolean;
src: string; soundType: SoundType;
}; };
export class Sound { export class Sound {
static sounds = new Map(); static sounds = new Map<SoundType, AudioBuffer>();
private static context: AudioContext | undefined; private static context: AudioContext | undefined;
@ -17,27 +29,29 @@ export class Sound {
private node?: AudioBufferSourceNode; private node?: AudioBufferSourceNode;
private readonly src: string; private readonly soundType: SoundType;
constructor(options: SoundOpts) { constructor(options: SoundOpts) {
this.loop = Boolean(options.loop); this.loop = Boolean(options.loop);
this.src = options.src; this.soundType = options.soundType;
} }
async play(): Promise<void> { async play(): Promise<void> {
if (!Sound.sounds.has(this.src)) { let soundBuffer = Sound.sounds.get(this.soundType);
if (!soundBuffer) {
try { try {
const buffer = await Sound.loadSoundFile(this.src); const src = Sound.getSrc(this.soundType);
const buffer = await Sound.loadSoundFile(src);
const decodedBuffer = await this.context.decodeAudioData(buffer); const decodedBuffer = await this.context.decodeAudioData(buffer);
Sound.sounds.set(this.src, decodedBuffer); Sound.sounds.set(this.soundType, decodedBuffer);
soundBuffer = decodedBuffer;
} catch (err) { } catch (err) {
log.error(`Sound error: ${err}`); log.error(`Sound error: ${err}`);
return; return;
} }
} }
const soundBuffer = Sound.sounds.get(this.src);
const soundNode = this.context.createBufferSource(); const soundNode = this.context.createBufferSource();
soundNode.buffer = soundBuffer; soundNode.buffer = soundBuffer;
@ -87,4 +101,40 @@ export class Sound {
xhr.send(); xhr.send();
}); });
} }
static getSrc(soundStyle: SoundType): string {
if (soundStyle === SoundType.CallingHangUp) {
return 'sounds/navigation-cancel.ogg';
}
if (soundStyle === SoundType.CallingPresenting) {
return 'sounds/navigation_selection-complete-celebration.ogg';
}
if (soundStyle === SoundType.Pop) {
return 'sounds/pop.wav';
}
if (soundStyle === SoundType.TriTone) {
return 'sounds/notification.ogg';
}
if (soundStyle === SoundType.Ringtone) {
return 'sounds/ringtone_minimal.ogg';
}
if (soundStyle === SoundType.VoiceNoteEnd) {
return 'sounds/state-change_confirm-down.ogg';
}
if (soundStyle === SoundType.VoiceNoteStart) {
return 'sounds/state-change_confirm-up.ogg';
}
if (soundStyle === SoundType.Whoosh) {
return 'sounds/whoosh.wav';
}
throw missingCaseError(soundStyle);
}
} }

View file

@ -3,7 +3,7 @@
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import { MINUTE } from './durations'; import { MINUTE } from './durations';
import { Sound } from './Sound'; import { Sound, SoundType } from './Sound';
const ringtoneEventQueue = new PQueue({ const ringtoneEventQueue = new PQueue({
concurrency: 1, concurrency: 1,
@ -21,7 +21,7 @@ class CallingTones {
} }
const tone = new Sound({ const tone = new Sound({
src: 'sounds/navigation-cancel.ogg', soundType: SoundType.CallingHangUp,
}); });
await tone.play(); await tone.play();
} }
@ -40,7 +40,7 @@ class CallingTones {
this.ringtone = new Sound({ this.ringtone = new Sound({
loop: true, loop: true,
src: 'sounds/ringtone_minimal.ogg', soundType: SoundType.Ringtone,
}); });
await this.ringtone.play(); await this.ringtone.play();
@ -63,7 +63,7 @@ class CallingTones {
} }
const tone = new Sound({ const tone = new Sound({
src: 'sounds/navigation_selection-complete-celebration.ogg', soundType: SoundType.CallingPresenting,
}); });
await tone.play(); await tone.play();

View file

@ -50,6 +50,7 @@ type NotificationSettingType = 'message' | 'name' | 'count' | 'off';
export type IPCEventsValuesType = { export type IPCEventsValuesType = {
alwaysRelayCalls: boolean | undefined; alwaysRelayCalls: boolean | undefined;
audioNotification: boolean | undefined; audioNotification: boolean | undefined;
audioMessage: boolean;
autoDownloadUpdate: boolean; autoDownloadUpdate: boolean;
autoLaunch: boolean; autoLaunch: boolean;
callRingtoneNotification: boolean; callRingtoneNotification: boolean;
@ -371,6 +372,8 @@ export function createIPCEvents(
window.storage.get('notification-draw-attention', false), window.storage.get('notification-draw-attention', false),
setNotificationDrawAttention: value => setNotificationDrawAttention: value =>
window.storage.put('notification-draw-attention', value), window.storage.put('notification-draw-attention', value),
getAudioMessage: () => window.storage.get('audioMessage', false),
setAudioMessage: value => window.storage.put('audioMessage', value),
getAudioNotification: () => window.storage.get('audio-notification'), getAudioNotification: () => window.storage.get('audio-notification'),
setAudioNotification: value => setAudioNotification: value =>
window.storage.put('audio-notification', value), window.storage.put('audio-notification', value),

View file

@ -38,6 +38,7 @@ installCallback('shouldShowStoriesSettings');
installCallback('syncRequest'); installCallback('syncRequest');
installSetting('alwaysRelayCalls'); installSetting('alwaysRelayCalls');
installSetting('audioMessage');
installSetting('audioNotification'); installSetting('audioNotification');
installSetting('autoDownloadUpdate'); installSetting('autoDownloadUpdate');
installSetting('autoLaunch'); installSetting('autoLaunch');

View file

@ -44,6 +44,7 @@ SettingsWindowProps.onRender(
hasLinkPreviews, hasLinkPreviews,
hasMediaCameraPermissions, hasMediaCameraPermissions,
hasMediaPermissions, hasMediaPermissions,
hasMessageAudio,
hasMinimizeToAndStartInSystemTray, hasMinimizeToAndStartInSystemTray,
hasMinimizeToSystemTray, hasMinimizeToSystemTray,
hasNotificationAttention, hasNotificationAttention,
@ -55,7 +56,6 @@ SettingsWindowProps.onRender(
hasTextFormatting, hasTextFormatting,
hasTypingIndicators, hasTypingIndicators,
initialSpellCheckSetting, initialSpellCheckSetting,
isAudioNotificationsSupported,
isAutoDownloadUpdatesSupported, isAutoDownloadUpdatesSupported,
isAutoLaunchSupported, isAutoLaunchSupported,
isFormattingFlagEnabled, isFormattingFlagEnabled,
@ -80,6 +80,7 @@ SettingsWindowProps.onRender(
onLastSyncTimeChange, onLastSyncTimeChange,
onMediaCameraPermissionsChange, onMediaCameraPermissionsChange,
onMediaPermissionsChange, onMediaPermissionsChange,
onMessageAudioChange,
onMinimizeToAndStartInSystemTrayChange, onMinimizeToAndStartInSystemTrayChange,
onMinimizeToSystemTrayChange, onMinimizeToSystemTrayChange,
onNotificationAttentionChange, onNotificationAttentionChange,
@ -141,6 +142,7 @@ SettingsWindowProps.onRender(
hasLinkPreviews={hasLinkPreviews} hasLinkPreviews={hasLinkPreviews}
hasMediaCameraPermissions={hasMediaCameraPermissions} hasMediaCameraPermissions={hasMediaCameraPermissions}
hasMediaPermissions={hasMediaPermissions} hasMediaPermissions={hasMediaPermissions}
hasMessageAudio={hasMessageAudio}
hasMinimizeToAndStartInSystemTray={hasMinimizeToAndStartInSystemTray} hasMinimizeToAndStartInSystemTray={hasMinimizeToAndStartInSystemTray}
hasMinimizeToSystemTray={hasMinimizeToSystemTray} hasMinimizeToSystemTray={hasMinimizeToSystemTray}
hasNotificationAttention={hasNotificationAttention} hasNotificationAttention={hasNotificationAttention}
@ -153,7 +155,6 @@ SettingsWindowProps.onRender(
hasTypingIndicators={hasTypingIndicators} hasTypingIndicators={hasTypingIndicators}
i18n={i18n} i18n={i18n}
initialSpellCheckSetting={initialSpellCheckSetting} initialSpellCheckSetting={initialSpellCheckSetting}
isAudioNotificationsSupported={isAudioNotificationsSupported}
isAutoDownloadUpdatesSupported={isAutoDownloadUpdatesSupported} isAutoDownloadUpdatesSupported={isAutoDownloadUpdatesSupported}
isAutoLaunchSupported={isAutoLaunchSupported} isAutoLaunchSupported={isAutoLaunchSupported}
isFormattingFlagEnabled={isFormattingFlagEnabled} isFormattingFlagEnabled={isFormattingFlagEnabled}
@ -180,6 +181,7 @@ SettingsWindowProps.onRender(
onLastSyncTimeChange={onLastSyncTimeChange} onLastSyncTimeChange={onLastSyncTimeChange}
onMediaCameraPermissionsChange={onMediaCameraPermissionsChange} onMediaCameraPermissionsChange={onMediaCameraPermissionsChange}
onMediaPermissionsChange={onMediaPermissionsChange} onMediaPermissionsChange={onMediaPermissionsChange}
onMessageAudioChange={onMessageAudioChange}
onMinimizeToAndStartInSystemTrayChange={ onMinimizeToAndStartInSystemTrayChange={
onMinimizeToAndStartInSystemTrayChange onMinimizeToAndStartInSystemTrayChange
} }

View file

@ -20,6 +20,7 @@ function doneRendering() {
ipcRenderer.send('settings-done-rendering'); ipcRenderer.send('settings-done-rendering');
} }
const settingMessageAudio = createSetting('audioMessage');
const settingAudioNotification = createSetting('audioNotification'); const settingAudioNotification = createSetting('audioNotification');
const settingAutoDownloadUpdate = createSetting('autoDownloadUpdate'); const settingAutoDownloadUpdate = createSetting('autoDownloadUpdate');
const settingAutoLaunch = createSetting('autoLaunch'); const settingAutoLaunch = createSetting('autoLaunch');
@ -152,6 +153,7 @@ async function renderPreferences() {
hasLinkPreviews, hasLinkPreviews,
hasMediaCameraPermissions, hasMediaCameraPermissions,
hasMediaPermissions, hasMediaPermissions,
hasMessageAudio,
hasNotificationAttention, hasNotificationAttention,
hasReadReceipts, hasReadReceipts,
hasRelayCalls, hasRelayCalls,
@ -193,6 +195,7 @@ async function renderPreferences() {
hasLinkPreviews: settingLinkPreview.getValue(), hasLinkPreviews: settingLinkPreview.getValue(),
hasMediaCameraPermissions: settingMediaCameraPermissions.getValue(), hasMediaCameraPermissions: settingMediaCameraPermissions.getValue(),
hasMediaPermissions: settingMediaPermissions.getValue(), hasMediaPermissions: settingMediaPermissions.getValue(),
hasMessageAudio: settingMessageAudio.getValue(),
hasNotificationAttention: settingNotificationDrawAttention.getValue(), hasNotificationAttention: settingNotificationDrawAttention.getValue(),
hasReadReceipts: settingReadReceipts.getValue(), hasReadReceipts: settingReadReceipts.getValue(),
hasRelayCalls: settingRelayCalls.getValue(), hasRelayCalls: settingRelayCalls.getValue(),
@ -253,6 +256,7 @@ async function renderPreferences() {
hasLinkPreviews, hasLinkPreviews,
hasMediaCameraPermissions, hasMediaCameraPermissions,
hasMediaPermissions, hasMediaPermissions,
hasMessageAudio,
hasMinimizeToAndStartInSystemTray, hasMinimizeToAndStartInSystemTray,
hasMinimizeToSystemTray, hasMinimizeToSystemTray,
hasNotificationAttention, hasNotificationAttention,
@ -293,7 +297,6 @@ async function renderPreferences() {
shouldShowStoriesSettings, shouldShowStoriesSettings,
// Limited support features // Limited support features
isAudioNotificationsSupported: Settings.isAudioNotificationSupported(OS),
isAutoDownloadUpdatesSupported: Settings.isAutoDownloadUpdatesSupported(OS), isAutoDownloadUpdatesSupported: Settings.isAutoDownloadUpdatesSupported(OS),
isAutoLaunchSupported: Settings.isAutoLaunchSupported(OS), isAutoLaunchSupported: Settings.isAutoLaunchSupported(OS),
isHideMenuBarSupported: Settings.isHideMenuBarSupported(OS), isHideMenuBarSupported: Settings.isHideMenuBarSupported(OS),
@ -347,6 +350,7 @@ async function renderPreferences() {
onMediaCameraPermissionsChange: attachRenderCallback( onMediaCameraPermissionsChange: attachRenderCallback(
settingMediaCameraPermissions.setValue settingMediaCameraPermissions.setValue
), ),
onMessageAudioChange: attachRenderCallback(settingMessageAudio.setValue),
onMinimizeToAndStartInSystemTrayChange: attachRenderCallback( onMinimizeToAndStartInSystemTrayChange: attachRenderCallback(
async (value: boolean) => { async (value: boolean) => {
await settingSystemTraySetting.setValue( await settingSystemTraySetting.setValue(