Notifications: Play sound when notifications are turned off (#1553)

This commit is contained in:
Alexander Zinchuk 2021-11-19 03:22:32 +03:00
parent 7ea9dd116f
commit 04f8d03e82
4 changed files with 39 additions and 33 deletions

View File

@ -1,4 +1,5 @@
import { ChangeEvent } from 'react'; import { ChangeEvent } from 'react';
import useDebounce from '../../../hooks/useDebounce';
import React, { import React, {
FC, memo, useCallback, useEffect, FC, memo, useCallback, useEffect,
} from '../../../lib/teact/teact'; } from '../../../lib/teact/teact';
@ -10,6 +11,7 @@ import { SettingsScreens } from '../../../types';
import { pick } from '../../../util/iteratees'; import { pick } from '../../../util/iteratees';
import useLang from '../../../hooks/useLang'; import useLang from '../../../hooks/useLang';
import useHistoryBack from '../../../hooks/useHistoryBack'; import useHistoryBack from '../../../hooks/useHistoryBack';
import { playNotifySound } from '../../../util/notifications';
import Checkbox from '../../ui/Checkbox'; import Checkbox from '../../ui/Checkbox';
import RangeSlider from '../../ui/RangeSlider'; import RangeSlider from '../../ui/RangeSlider';
@ -61,6 +63,8 @@ const SettingsNotifications: FC<OwnProps & StateProps & DispatchProps> = ({
loadNotificationSettings(); loadNotificationSettings();
}, [loadNotificationSettings]); }, [loadNotificationSettings]);
const runDebounced = useDebounce(500, false, true);
const handleSettingsChange = useCallback(( const handleSettingsChange = useCallback((
e: ChangeEvent<HTMLInputElement>, e: ChangeEvent<HTMLInputElement>,
peerType: 'contact' | 'group' | 'broadcast', peerType: 'contact' | 'group' | 'broadcast',
@ -123,12 +127,12 @@ const SettingsNotifications: FC<OwnProps & StateProps & DispatchProps> = ({
<div className="settings-item-slider"> <div className="settings-item-slider">
<RangeSlider <RangeSlider
label="Sound" label="Sound"
disabled={!hasWebNotifications}
min={0} min={0}
max={10} max={10}
value={notificationSoundVolume} value={notificationSoundVolume}
onChange={(volume) => { onChange={(volume) => {
updateWebNotificationSettings({ notificationSoundVolume: volume }); updateWebNotificationSettings({ notificationSoundVolume: volume });
runDebounced(() => playNotifySound(undefined, volume));
}} }}
/> />
</div> </div>

View File

@ -4,7 +4,7 @@ import { ApiUpdate, MAIN_THREAD_ID } from '../../../api/types';
import { ARCHIVED_FOLDER_ID, MAX_ACTIVE_PINNED_CHATS } from '../../../config'; import { ARCHIVED_FOLDER_ID, MAX_ACTIVE_PINNED_CHATS } from '../../../config';
import { pick } from '../../../util/iteratees'; import { pick } from '../../../util/iteratees';
import { closeMessageNotifications, showNewMessageNotification } from '../../../util/notifications'; import { closeMessageNotifications, notifyAboutNewMessage } from '../../../util/notifications';
import { updateAppBadge } from '../../../util/appBadge'; import { updateAppBadge } from '../../../util/appBadge';
import { import {
updateChat, updateChat,
@ -20,7 +20,6 @@ import {
selectChatListType, selectChatListType,
selectCurrentMessageList, selectCurrentMessageList,
selectCountNotMutedUnread, selectCountNotMutedUnread,
selectNotifySettings,
} from '../../selectors'; } from '../../selectors';
import { throttle } from '../../../util/schedulers'; import { throttle } from '../../../util/schedulers';
@ -131,15 +130,11 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
} }
updateAppBadge(selectCountNotMutedUnread(getGlobal())); updateAppBadge(selectCountNotMutedUnread(getGlobal()));
notifyAboutNewMessage({
const { hasWebNotifications } = selectNotifySettings(global); chat,
if (hasWebNotifications) { message,
showNewMessageNotification({ isActiveChat,
chat, });
message,
isActiveChat,
});
}
break; break;
} }

View File

@ -90,22 +90,24 @@ const expirationTime = 12 * 60 * 60 * 1000; // 12 hours
const soundPlayedDelay = 3 * 1000; const soundPlayedDelay = 3 * 1000;
const soundPlayedIds = new Set<string>(); const soundPlayedIds = new Set<string>();
async function playSound(id: string) { export async function playNotifySound(id?: string, volume?: number) {
if (soundPlayedIds.has(id)) return; if (id !== undefined && soundPlayedIds.has(id)) return;
const { notificationSoundVolume } = selectNotifySettings(getGlobal()); const { notificationSoundVolume } = selectNotifySettings(getGlobal());
const volume = notificationSoundVolume / 10; const currentVolume = volume ? volume / 10 : notificationSoundVolume / 10;
if (volume === 0) return; if (currentVolume === 0) return;
const audio = new Audio('./notification.mp3'); const audio = new Audio('./notification.mp3');
audio.volume = volume; audio.volume = currentVolume;
audio.setAttribute('mozaudiochannel', 'notification'); audio.setAttribute('mozaudiochannel', 'notification');
audio.addEventListener('ended', () => { if (id !== undefined) {
soundPlayedIds.add(id); audio.addEventListener('ended', () => {
}, { once: true }); soundPlayedIds.add(id);
}, { once: true });
setTimeout(() => { setTimeout(() => {
soundPlayedIds.delete(id); soundPlayedIds.delete(id);
}, soundPlayedDelay); }, soundPlayedDelay);
}
try { try {
await audio.play(); await audio.play();
@ -117,7 +119,7 @@ async function playSound(id: string) {
} }
} }
export const playNotificationSound = debounce(playSound, 1000, true, false); export const playNotifySoundDebounced = debounce(playNotifySound, 1000, true, false);
function checkIfShouldResubscribe(subscription: PushSubscription | null) { function checkIfShouldResubscribe(subscription: PushSubscription | null) {
const global = getGlobal(); const global = getGlobal();
@ -168,7 +170,7 @@ let areSettingsLoaded = false;
// Load notification settings from the api // Load notification settings from the api
async function loadNotificationSettings() { async function loadNotificationSettings() {
if (areSettingsLoaded) return; if (areSettingsLoaded) return selectNotifySettings(getGlobal());
const [resultSettings, resultExceptions] = await Promise.all([ const [resultSettings, resultExceptions] = await Promise.all([
callApi('fetchNotificationSettings', { callApi('fetchNotificationSettings', {
serverTimeOffset: getGlobal().serverTimeOffset, serverTimeOffset: getGlobal().serverTimeOffset,
@ -177,7 +179,7 @@ async function loadNotificationSettings() {
serverTimeOffset: getGlobal().serverTimeOffset, serverTimeOffset: getGlobal().serverTimeOffset,
}), }),
]); ]);
if (!resultSettings) return; if (!resultSettings) return selectNotifySettings(getGlobal());
let global = replaceSettings(getGlobal(), resultSettings); let global = replaceSettings(getGlobal(), resultSettings);
if (resultExceptions) { if (resultExceptions) {
@ -185,6 +187,7 @@ async function loadNotificationSettings() {
} }
setGlobal(global); setGlobal(global);
areSettingsLoaded = true; areSettingsLoaded = true;
return selectNotifySettings(global);
} }
export async function subscribe() { export async function subscribe() {
@ -313,17 +316,21 @@ async function getAvatar(chat: ApiChat) {
return mediaData; return mediaData;
} }
export async function showNewMessageNotification({ export async function notifyAboutNewMessage({
chat, chat,
message, message,
isActiveChat, isActiveChat,
}: { chat: ApiChat; message: Partial<ApiMessage>; isActiveChat: boolean }) { }: { chat: ApiChat; message: Partial<ApiMessage>; isActiveChat: boolean }) {
const { hasWebNotifications } = await loadNotificationSettings();
if (!checkIfShouldNotify(chat, isActiveChat)) return;
if (!hasWebNotifications) {
// only play sound if web notifications are disabled
playNotifySoundDebounced(String(message.id) || chat.id);
return;
}
if (!checkIfNotificationsSupported()) return; if (!checkIfNotificationsSupported()) return;
if (!message.id) return; if (!message.id) return;
await loadNotificationSettings();
if (!checkIfShouldNotify(chat, isActiveChat)) return;
const { const {
title, title,
body, body,
@ -373,7 +380,7 @@ export async function showNewMessageNotification({
// Play sound when notification is displayed // Play sound when notification is displayed
notification.onshow = () => { notification.onshow = () => {
playNotificationSound(String(message.id) || chat.id); playNotifySoundDebounced(String(message.id) || chat.id);
}; };
} }
} }

View File

@ -1,7 +1,7 @@
import { DEBUG } from '../config'; import { DEBUG } from '../config';
import { getDispatch } from '../lib/teact/teactn'; import { getDispatch } from '../lib/teact/teactn';
import { IS_ANDROID, IS_IOS, IS_SERVICE_WORKER_SUPPORTED } from './environment'; import { IS_ANDROID, IS_IOS, IS_SERVICE_WORKER_SUPPORTED } from './environment';
import { notifyClientReady, playNotificationSound } from './notifications'; import { notifyClientReady, playNotifySoundDebounced } from './notifications';
type WorkerAction = { type WorkerAction = {
type: string; type: string;
@ -23,7 +23,7 @@ function handleWorkerMessage(e: MessageEvent) {
} }
break; break;
case 'playNotificationSound': case 'playNotificationSound':
playNotificationSound(action.payload.id); playNotifySoundDebounced(action.payload.id);
break; break;
} }
} }