Notifications: Play sound when notifications are turned off (#1553)
This commit is contained in:
parent
7ea9dd116f
commit
04f8d03e82
@ -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>
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user