From 04f8d03e824a7321219acbf5462b76912b141e38 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 19 Nov 2021 03:22:32 +0300 Subject: [PATCH] Notifications: Play sound when notifications are turned off (#1553) --- .../left/settings/SettingsNotifications.tsx | 6 ++- src/modules/actions/apiUpdaters/chats.ts | 17 +++---- src/util/notifications.ts | 45 +++++++++++-------- src/util/setupServiceWorker.ts | 4 +- 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/components/left/settings/SettingsNotifications.tsx b/src/components/left/settings/SettingsNotifications.tsx index 20052e79..fc063594 100644 --- a/src/components/left/settings/SettingsNotifications.tsx +++ b/src/components/left/settings/SettingsNotifications.tsx @@ -1,4 +1,5 @@ import { ChangeEvent } from 'react'; +import useDebounce from '../../../hooks/useDebounce'; import React, { FC, memo, useCallback, useEffect, } from '../../../lib/teact/teact'; @@ -10,6 +11,7 @@ import { SettingsScreens } from '../../../types'; import { pick } from '../../../util/iteratees'; import useLang from '../../../hooks/useLang'; import useHistoryBack from '../../../hooks/useHistoryBack'; +import { playNotifySound } from '../../../util/notifications'; import Checkbox from '../../ui/Checkbox'; import RangeSlider from '../../ui/RangeSlider'; @@ -61,6 +63,8 @@ const SettingsNotifications: FC = ({ loadNotificationSettings(); }, [loadNotificationSettings]); + const runDebounced = useDebounce(500, false, true); + const handleSettingsChange = useCallback(( e: ChangeEvent, peerType: 'contact' | 'group' | 'broadcast', @@ -123,12 +127,12 @@ const SettingsNotifications: FC = ({
{ updateWebNotificationSettings({ notificationSoundVolume: volume }); + runDebounced(() => playNotifySound(undefined, volume)); }} />
diff --git a/src/modules/actions/apiUpdaters/chats.ts b/src/modules/actions/apiUpdaters/chats.ts index 11e799dc..5074c5f9 100644 --- a/src/modules/actions/apiUpdaters/chats.ts +++ b/src/modules/actions/apiUpdaters/chats.ts @@ -4,7 +4,7 @@ import { ApiUpdate, MAIN_THREAD_ID } from '../../../api/types'; import { ARCHIVED_FOLDER_ID, MAX_ACTIVE_PINNED_CHATS } from '../../../config'; import { pick } from '../../../util/iteratees'; -import { closeMessageNotifications, showNewMessageNotification } from '../../../util/notifications'; +import { closeMessageNotifications, notifyAboutNewMessage } from '../../../util/notifications'; import { updateAppBadge } from '../../../util/appBadge'; import { updateChat, @@ -20,7 +20,6 @@ import { selectChatListType, selectCurrentMessageList, selectCountNotMutedUnread, - selectNotifySettings, } from '../../selectors'; import { throttle } from '../../../util/schedulers'; @@ -131,15 +130,11 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => { } updateAppBadge(selectCountNotMutedUnread(getGlobal())); - - const { hasWebNotifications } = selectNotifySettings(global); - if (hasWebNotifications) { - showNewMessageNotification({ - chat, - message, - isActiveChat, - }); - } + notifyAboutNewMessage({ + chat, + message, + isActiveChat, + }); break; } diff --git a/src/util/notifications.ts b/src/util/notifications.ts index 66e5d54e..4af6e1d8 100644 --- a/src/util/notifications.ts +++ b/src/util/notifications.ts @@ -90,22 +90,24 @@ const expirationTime = 12 * 60 * 60 * 1000; // 12 hours const soundPlayedDelay = 3 * 1000; const soundPlayedIds = new Set(); -async function playSound(id: string) { - if (soundPlayedIds.has(id)) return; +export async function playNotifySound(id?: string, volume?: number) { + if (id !== undefined && soundPlayedIds.has(id)) return; const { notificationSoundVolume } = selectNotifySettings(getGlobal()); - const volume = notificationSoundVolume / 10; - if (volume === 0) return; + const currentVolume = volume ? volume / 10 : notificationSoundVolume / 10; + if (currentVolume === 0) return; const audio = new Audio('./notification.mp3'); - audio.volume = volume; + audio.volume = currentVolume; audio.setAttribute('mozaudiochannel', 'notification'); - audio.addEventListener('ended', () => { - soundPlayedIds.add(id); - }, { once: true }); + if (id !== undefined) { + audio.addEventListener('ended', () => { + soundPlayedIds.add(id); + }, { once: true }); - setTimeout(() => { - soundPlayedIds.delete(id); - }, soundPlayedDelay); + setTimeout(() => { + soundPlayedIds.delete(id); + }, soundPlayedDelay); + } try { 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) { const global = getGlobal(); @@ -168,7 +170,7 @@ let areSettingsLoaded = false; // Load notification settings from the api async function loadNotificationSettings() { - if (areSettingsLoaded) return; + if (areSettingsLoaded) return selectNotifySettings(getGlobal()); const [resultSettings, resultExceptions] = await Promise.all([ callApi('fetchNotificationSettings', { serverTimeOffset: getGlobal().serverTimeOffset, @@ -177,7 +179,7 @@ async function loadNotificationSettings() { serverTimeOffset: getGlobal().serverTimeOffset, }), ]); - if (!resultSettings) return; + if (!resultSettings) return selectNotifySettings(getGlobal()); let global = replaceSettings(getGlobal(), resultSettings); if (resultExceptions) { @@ -185,6 +187,7 @@ async function loadNotificationSettings() { } setGlobal(global); areSettingsLoaded = true; + return selectNotifySettings(global); } export async function subscribe() { @@ -313,17 +316,21 @@ async function getAvatar(chat: ApiChat) { return mediaData; } -export async function showNewMessageNotification({ +export async function notifyAboutNewMessage({ chat, message, isActiveChat, }: { chat: ApiChat; message: Partial; 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 (!message.id) return; - await loadNotificationSettings(); - if (!checkIfShouldNotify(chat, isActiveChat)) return; - const { title, body, @@ -373,7 +380,7 @@ export async function showNewMessageNotification({ // Play sound when notification is displayed notification.onshow = () => { - playNotificationSound(String(message.id) || chat.id); + playNotifySoundDebounced(String(message.id) || chat.id); }; } } diff --git a/src/util/setupServiceWorker.ts b/src/util/setupServiceWorker.ts index 7ed4da3c..6d78697d 100644 --- a/src/util/setupServiceWorker.ts +++ b/src/util/setupServiceWorker.ts @@ -1,7 +1,7 @@ import { DEBUG } from '../config'; import { getDispatch } from '../lib/teact/teactn'; import { IS_ANDROID, IS_IOS, IS_SERVICE_WORKER_SUPPORTED } from './environment'; -import { notifyClientReady, playNotificationSound } from './notifications'; +import { notifyClientReady, playNotifySoundDebounced } from './notifications'; type WorkerAction = { type: string; @@ -23,7 +23,7 @@ function handleWorkerMessage(e: MessageEvent) { } break; case 'playNotificationSound': - playNotificationSound(action.payload.id); + playNotifySoundDebounced(action.payload.id); break; } }