[MM-57080] Add Channel-specific notification sound options in new Notifications settings modal (#27730)

Этот коммит содержится в:
M-ZubairAhmed
2024-08-23 17:55:27 +00:00
коммит произвёл GitHub
родитель e121bebba5
Коммит b0d58bb65e
14 изменённых файлов: 928 добавлений и 306 удалений

Просмотреть файл

@@ -144,8 +144,8 @@ describe('CRT Desktop notifications', () => {
cy.visit(testChannelUrl);
cy.uiOpenChannelMenu('Notification Preferences');
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-mention').should('be.visible').click().then(() => {
cy.get('[data-testid="desktopReplyThreads"]').should('be.visible').click();
cy.get('#desktopNotification-mention').scrollIntoView().should('be.visible').click().then(() => {
cy.get('[data-testid="desktopReplyThreads"]').scrollIntoView().should('be.visible').click();
});
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('[data-testid="desktopReplyThreads"]').should('be.visible').click();
cy.get('.channel-notifications-settings-modal__body').get('[data-testid="sameMobileSettingsDesktop"]').scrollIntoView().click().should('be.checked').then(() => {
@@ -174,7 +174,7 @@ describe('CRT Desktop notifications', () => {
// Setup notification spy
spyNotificationAs('notifySpy', 'granted');
cy.uiOpenChannelMenu('Notification Preferences');
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-mention').should('be.visible').click();
cy.get('#desktopNotification-mention').scrollIntoView().should('be.visible').click();
// # Save the changes
cy.findByText('Save').should('be.visible').click();

Просмотреть файл

@@ -220,6 +220,87 @@ Object {
</fieldset>
</div>
</div>
<div
class="mm-modal-generic-section-item"
>
<h4
class="mm-modal-generic-section-item__title"
data-testid="mm-modal-generic-section-item__title"
>
Sounds
</h4>
<div
class="mm-modal-generic-section-item__content inline"
data-testid="mm-modal-generic-section-item__content"
>
<fieldset
class="mm-modal-generic-section-item__fieldset-checkbox-ctr"
>
<label
class="mm-modal-generic-section-item__fieldset-checkbox"
>
<input
checked=""
class="mm-modal-generic-section-item__input-checkbox"
data-testid="desktopNotificationSoundsCheckbox"
name="desktopNotificationSoundsCheckbox"
type="checkbox"
/>
Message notification sounds
</label>
</fieldset>
<fieldset
class="mm-modal-generic-section-item__fieldset-react-select"
>
<div
class="react-select singleSelect react-select-top css-2b097c-container"
id="desktopNotificationSoundsSelect"
>
<div
class="react-select__control css-yk16xz-control"
>
<div
class="react-select__value-container react-select__value-container--has-value css-1hwfws3"
>
<div
class="react-select__single-value css-1uccc91-singleValue"
>
Bing
</div>
<input
class="css-62g3xt-dummyInput"
id="desktopNotificationSoundsSelectInputId"
readonly=""
tabindex="0"
value=""
/>
</div>
<div
class="react-select__indicators css-1hb7zxy-IndicatorsContainer"
>
<div
aria-hidden="true"
class="react-select__indicator react-select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-6q0nyr-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
</div>
</section>
<div
@@ -237,6 +318,7 @@ Object {
Mobile Notifications
<button
class="channel-notifications-settings-modal__reset-btn"
data-testid="resetToDefaultButton-push"
>
<svg
fill="currentColor"
@@ -579,6 +661,7 @@ Object {
Desktop Notifications
<button
class="channel-notifications-settings-modal__reset-btn"
data-testid="resetToDefaultButton-desktop"
>
<svg
fill="currentColor"
@@ -678,6 +761,7 @@ Object {
Mobile Notifications
<button
class="channel-notifications-settings-modal__reset-btn"
data-testid="resetToDefaultButton-push"
>
<svg
fill="currentColor"
@@ -1085,6 +1169,87 @@ Object {
</fieldset>
</div>
</div>
<div
class="mm-modal-generic-section-item"
>
<h4
class="mm-modal-generic-section-item__title"
data-testid="mm-modal-generic-section-item__title"
>
Sounds
</h4>
<div
class="mm-modal-generic-section-item__content inline"
data-testid="mm-modal-generic-section-item__content"
>
<fieldset
class="mm-modal-generic-section-item__fieldset-checkbox-ctr"
>
<label
class="mm-modal-generic-section-item__fieldset-checkbox"
>
<input
checked=""
class="mm-modal-generic-section-item__input-checkbox"
data-testid="desktopNotificationSoundsCheckbox"
name="desktopNotificationSoundsCheckbox"
type="checkbox"
/>
Message notification sounds
</label>
</fieldset>
<fieldset
class="mm-modal-generic-section-item__fieldset-react-select"
>
<div
class="react-select singleSelect react-select-top css-2b097c-container"
id="desktopNotificationSoundsSelect"
>
<div
class="react-select__control css-yk16xz-control"
>
<div
class="react-select__value-container react-select__value-container--has-value css-1hwfws3"
>
<div
class="react-select__single-value css-1uccc91-singleValue"
>
Bing
</div>
<input
class="css-62g3xt-dummyInput"
id="desktopNotificationSoundsSelectInputId"
readonly=""
tabindex="0"
value=""
/>
</div>
<div
class="react-select__indicators css-1hb7zxy-IndicatorsContainer"
>
<div
aria-hidden="true"
class="react-select__indicator react-select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-6q0nyr-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
</div>
</section>
<div
@@ -1102,6 +1267,7 @@ Object {
Mobile Notifications
<button
class="channel-notifications-settings-modal__reset-btn"
data-testid="resetToDefaultButton-push"
>
<svg
fill="currentColor"
@@ -1764,6 +1930,87 @@ Object {
</fieldset>
</div>
</div>
<div
class="mm-modal-generic-section-item"
>
<h4
class="mm-modal-generic-section-item__title"
data-testid="mm-modal-generic-section-item__title"
>
Sounds
</h4>
<div
class="mm-modal-generic-section-item__content inline"
data-testid="mm-modal-generic-section-item__content"
>
<fieldset
class="mm-modal-generic-section-item__fieldset-checkbox-ctr"
>
<label
class="mm-modal-generic-section-item__fieldset-checkbox"
>
<input
checked=""
class="mm-modal-generic-section-item__input-checkbox"
data-testid="desktopNotificationSoundsCheckbox"
name="desktopNotificationSoundsCheckbox"
type="checkbox"
/>
Message notification sounds
</label>
</fieldset>
<fieldset
class="mm-modal-generic-section-item__fieldset-react-select"
>
<div
class="react-select singleSelect react-select-top css-2b097c-container"
id="desktopNotificationSoundsSelect"
>
<div
class="react-select__control css-yk16xz-control"
>
<div
class="react-select__value-container react-select__value-container--has-value css-1hwfws3"
>
<div
class="react-select__single-value css-1uccc91-singleValue"
>
Bing
</div>
<input
class="css-62g3xt-dummyInput"
id="desktopNotificationSoundsSelectInputId"
readonly=""
tabindex="0"
value=""
/>
</div>
<div
class="react-select__indicators css-1hb7zxy-IndicatorsContainer"
>
<div
aria-hidden="true"
class="react-select__indicator react-select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-6q0nyr-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
</div>
</section>
<div
@@ -1781,6 +2028,7 @@ Object {
Mobile Notifications
<button
class="channel-notifications-settings-modal__reset-btn"
data-testid="resetToDefaultButton-push"
>
<svg
fill="currentColor"
@@ -2132,6 +2380,87 @@ Object {
</fieldset>
</div>
</div>
<div
class="mm-modal-generic-section-item"
>
<h4
class="mm-modal-generic-section-item__title"
data-testid="mm-modal-generic-section-item__title"
>
Sounds
</h4>
<div
class="mm-modal-generic-section-item__content inline"
data-testid="mm-modal-generic-section-item__content"
>
<fieldset
class="mm-modal-generic-section-item__fieldset-checkbox-ctr"
>
<label
class="mm-modal-generic-section-item__fieldset-checkbox"
>
<input
checked=""
class="mm-modal-generic-section-item__input-checkbox"
data-testid="desktopNotificationSoundsCheckbox"
name="desktopNotificationSoundsCheckbox"
type="checkbox"
/>
Message notification sounds
</label>
</fieldset>
<fieldset
class="mm-modal-generic-section-item__fieldset-react-select"
>
<div
class="react-select singleSelect react-select-top css-2b097c-container"
id="desktopNotificationSoundsSelect"
>
<div
class="react-select__control css-yk16xz-control"
>
<div
class="react-select__value-container react-select__value-container--has-value css-1hwfws3"
>
<div
class="react-select__single-value css-1uccc91-singleValue"
>
Bing
</div>
<input
class="css-62g3xt-dummyInput"
id="desktopNotificationSoundsSelectInputId"
readonly=""
tabindex="0"
value=""
/>
</div>
<div
class="react-select__indicators css-1hb7zxy-IndicatorsContainer"
>
<div
aria-hidden="true"
class="react-select__indicator react-select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-6q0nyr-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
</div>
</section>
<div
@@ -2149,6 +2478,7 @@ Object {
Mobile Notifications
<button
class="channel-notifications-settings-modal__reset-btn"
data-testid="resetToDefaultButton-push"
>
<svg
fill="currentColor"

Просмотреть файл

@@ -24,6 +24,8 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
channelMember: {
notify_props: {
desktop: NotificationLevels.ALL,
desktop_sound: 'on',
desktop_notification_sound: 'Bing',
mark_unread: NotificationLevels.ALL,
push: NotificationLevels.DEFAULT,
ignore_channel_mentions: IgnoreChannelMentions.DEFAULT,
@@ -35,6 +37,8 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
id: 'current_user_id',
notify_props: {
desktop: NotificationLevels.ALL,
desktop_sound: 'true',
desktop_notification_sound: 'Bing',
desktop_threads: NotificationLevels.ALL,
} as UserNotifyProps,
}),
@@ -73,6 +77,8 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
ignore_channel_mentions: 'off',
mark_unread: 'mention',
push: 'all',
desktop_sound: 'on',
desktop_notification_sound: 'Bing',
},
),
);
@@ -98,6 +104,8 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
mark_unread:
baseProps.channelMember?.notify_props.mark_unread,
push: 'all',
desktop_sound: 'on',
desktop_notification_sound: 'Bing',
},
),
);
@@ -139,12 +147,52 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
ignore_channel_mentions: 'off',
mark_unread: 'all',
push: 'all',
desktop_sound: 'on',
desktop_notification_sound: 'Bing',
},
),
);
expect(wrapper).toMatchSnapshot();
});
test('should disable message notification sound if option is unchecked', async () => {
renderWithContext(<ChannelNotificationsModal {...baseProps}/>);
// Since the default value is on, we will uncheck the checkbox
fireEvent.click(screen.getByTestId('desktopNotificationSoundsCheckbox'));
expect(screen.getByTestId('desktopNotificationSoundsCheckbox')).not.toBeChecked();
fireEvent.click(screen.getByRole('button', {name: /Save/i}));
await waitFor(() => {
expect(baseProps.actions.updateChannelNotifyProps).toHaveBeenCalledWith(
'current_user_id',
'channel_id',
{
desktop: 'all',
ignore_channel_mentions: 'off',
mark_unread: 'all',
push: 'all',
desktop_sound: 'off',
desktop_notification_sound: 'Bing',
},
);
});
});
test('should default to user desktop notification sound if reset to default is clicked', async () => {
renderWithContext(<ChannelNotificationsModal {...baseProps}/>);
// Since the default value is on, we will uncheck the checkbox
fireEvent.click(screen.getByTestId('desktopNotificationSoundsCheckbox'));
expect(screen.getByTestId('desktopNotificationSoundsCheckbox')).not.toBeChecked();
// Reset to default button is clicked
fireEvent.click(screen.getByTestId('resetToDefaultButton-desktop'));
// Verify that the checkbox is checked to default to user desktop notification sound
expect(screen.getByTestId('desktopNotificationSoundsCheckbox')).toBeChecked();
});
test('should save the options exactly same as Desktop for mobile if use same as desktop checkbox is checked', async () => {
const wrapper = renderWithContext(
<ChannelNotificationsModal {...baseProps}/>,
@@ -170,6 +218,8 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
ignore_channel_mentions: 'off',
mark_unread: 'all',
push: 'all',
desktop_sound: 'on',
desktop_notification_sound: 'Bing',
},
),
);
@@ -209,6 +259,8 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
ignore_channel_mentions: 'off',
mark_unread: 'all',
push: 'none',
desktop_sound: 'on',
desktop_notification_sound: 'Bing',
},
),
);
@@ -240,6 +292,8 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
push: 'all',
push_threads: 'default',
desktop_threads: 'all',
desktop_sound: 'on',
desktop_notification_sound: 'Bing',
},
),
);

Просмотреть файл

@@ -4,6 +4,7 @@
import React, {useCallback, useState} from 'react';
import {Modal} from 'react-bootstrap';
import {FormattedMessage, useIntl} from 'react-intl';
import type {ValueType} from 'react-select';
import {BellOffOutlineIcon, RefreshIcon} from '@mattermost/compass-icons/components';
import type {Channel, ChannelNotifyProps} from '@mattermost/types/channels';
@@ -11,14 +12,17 @@ import type {UserNotifyProps, UserProfile} from '@mattermost/types/users';
import AlertBanner from 'components/alert_banner';
import CheckboxSettingItem from 'components/widgets/modals/components/checkbox_setting_item';
import CheckboxWithSelectSettingItem from 'components/widgets/modals/components/checkbox_with_select_item';
import ModalHeader from 'components/widgets/modals/components/modal_header';
import ModalSection from 'components/widgets/modals/components/modal_section';
import RadioSettingItem from 'components/widgets/modals/components/radio_setting_item';
import type {Option} from 'components/widgets/modals/components/react_select_item';
import {IgnoreChannelMentions, NotificationLevels} from 'utils/constants';
import {IgnoreChannelMentions, NotificationLevels, DesktopSound} from 'utils/constants';
import {getValueOfNotificationSoundsSelect, notificationSoundKeys, stopTryNotificationRing, tryNotificationSound} from 'utils/notification_sounds';
import type {ChannelMemberNotifyProps} from './utils';
import utils from './utils';
import utils, {convertDesktopSoundNotifyPropFromUserToDesktop} from './utils';
import type {PropsFromRedux} from './index';
@@ -63,9 +67,27 @@ function getStateFromNotifyProps(currentUserNotifyProps: UserNotifyProps, channe
const desktop = channelMemberNotifyProps?.desktop === NotificationLevels.DEFAULT ? currentUserNotifyProps.desktop : (channelMemberNotifyProps?.desktop || currentUserNotifyProps.desktop);
const push = channelMemberNotifyProps?.push === NotificationLevels.DEFAULT ? currentUserNotifyProps.desktop : (channelMemberNotifyProps?.push || currentUserNotifyProps.push);
let desktopSound;
if (channelMemberNotifyProps && channelMemberNotifyProps.desktop_sound) {
desktopSound = channelMemberNotifyProps.desktop_sound;
} else {
desktopSound = convertDesktopSoundNotifyPropFromUserToDesktop(currentUserNotifyProps.desktop_sound);
}
let desktopNotificationSound;
if (channelMemberNotifyProps && channelMemberNotifyProps.desktop_notification_sound) {
desktopNotificationSound = channelMemberNotifyProps.desktop_notification_sound;
} else if (currentUserNotifyProps && currentUserNotifyProps.desktop_notification_sound) {
desktopNotificationSound = currentUserNotifyProps.desktop_notification_sound;
} else {
desktopNotificationSound = notificationSoundKeys[0] as ChannelNotifyProps['desktop_notification_sound'];
}
return {
desktop,
desktop_threads: channelMemberNotifyProps?.desktop_threads || NotificationLevels.ALL,
desktop_sound: desktopSound,
desktop_notification_sound: desktopNotificationSound,
mark_unread: channelMemberNotifyProps?.mark_unread || NotificationLevels.ALL,
push,
push_threads: channelMemberNotifyProps?.push_threads || NotificationLevels.ALL,
@@ -77,6 +99,8 @@ function getStateFromNotifyProps(currentUserNotifyProps: UserNotifyProps, channe
type SettingsType = {
desktop: ChannelNotifyProps['desktop'];
desktop_threads: ChannelNotifyProps['desktop_threads'];
desktop_sound: ChannelNotifyProps['desktop_sound'];
desktop_notification_sound: ChannelNotifyProps['desktop_notification_sound'];
mark_unread: ChannelNotifyProps['mark_unread'];
push: ChannelNotifyProps['push'];
push_threads: ChannelNotifyProps['push_threads'];
@@ -104,7 +128,7 @@ export default function ChannelNotificationsModal(props: Props) {
setSettings((prevSettings) => ({...prevSettings, push: prevSettings.desktop, push_threads: prevSettings.desktop_threads}));
}, []);
const MuteOrIgnoreSectionContent = (
const muteOrIgnoreSectionContent = (
<>
<CheckboxSettingItem
inputFieldTitle={
@@ -139,7 +163,16 @@ export default function ChannelNotificationsModal(props: Props) {
</>
);
const DesktopNotificationsSectionContent = (
const handleChangeForMessageNotificationSoundSelect = (selectedOption: ValueType<Option>) => {
stopTryNotificationRing();
if (selectedOption && 'value' in selectedOption) {
handleChange({desktop_notification_sound: ((selectedOption as Option).value)});
tryNotificationSound(selectedOption.value);
}
};
const desktopNotificationsSectionContent = (
<>
<RadioSettingItem
title={formatMessage({
@@ -165,11 +198,37 @@ export default function ChannelNotificationsModal(props: Props) {
/>
}
handleChange={(e) => handleChange({desktop_threads: e ? 'all' : 'mention'})}
/>}
/>
}
{settings.desktop !== 'none' && (
<CheckboxWithSelectSettingItem
title={formatMessage({
id: 'channel_notifications.desktopNotifications.title',
defaultMessage: 'Sounds',
})}
checkboxFieldTitle={
<FormattedMessage
id='channel_notifications.desktopNotifications.soundEnable'
defaultMessage='Message notification sounds'
/>
}
checkboxFieldValue={settings.desktop_sound === DesktopSound.ON}
checkboxFieldData={utils.desktopNotificationSoundsCheckboxFieldData}
handleCheckboxChange={(isChecked) => handleChange({desktop_sound: isChecked ? DesktopSound.ON : DesktopSound.OFF})}
selectFieldData={utils.desktopNotificationSoundsSelectFieldData}
selectFieldValue={getValueOfNotificationSoundsSelect(settings.desktop_notification_sound)}
isSelectDisabled={settings.desktop_sound !== 'on'}
selectPlaceholder={formatMessage({
id: 'channel_notifications.desktopNotifications.soundSelectPlaceholder',
defaultMessage: 'Select a sound',
})}
handleSelectChange={handleChangeForMessageNotificationSoundSelect}
/>
)}
</>
);
const MobileNotificationsSectionContent = (
const mobileNotificationsSectionContent = (
<>
<CheckboxSettingItem
inputFieldTitle={
@@ -214,7 +273,7 @@ export default function ChannelNotificationsModal(props: Props) {
</>
);
const AutoFollowThreadsSectionContent = (
const autoFollowThreadsSectionContent = (
<CheckboxSettingItem
inputFieldTitle={
<FormattedMessage
@@ -245,39 +304,57 @@ export default function ChannelNotificationsModal(props: Props) {
});
}
const resetToDefaultBtn = useCallback((settingName: string) => {
const defaultSettings = props.currentUser.notify_props;
const resetToDefault = (settingName: string) => {
if (settingName === 'desktop') {
setSettings({...settings, desktop: defaultSettings.desktop, desktop_threads: defaultSettings.desktop_threads || settings.desktop_threads});
}
if (settingName === 'push') {
setSettings({...settings, push: defaultSettings.desktop, push_threads: defaultSettings.push_threads || settings.push_threads});
}
const resetToDefaultBtn = useCallback((sectionName: string) => {
const userNotifyProps = {
...props.currentUser.notify_props,
desktop_notification_sound: props.currentUser.notify_props?.desktop_notification_sound ?? notificationSoundKeys[0] as ChannelNotifyProps['desktop_notification_sound'],
};
const isDesktopSameAsDefault = (defaultSettings.desktop === settings.desktop && defaultSettings.desktop_threads === settings.desktop_threads);
const isPushSameAsDefault = (defaultSettings.push === settings.push && defaultSettings.push_threads === settings.push_threads);
if ((settingName === 'desktop' && isDesktopSameAsDefault) || (settingName === 'push' && isPushSameAsDefault)) {
return <></>;
function resetToDefault(sectionName: string) {
if (sectionName === 'desktop') {
setSettings({
...settings,
desktop: userNotifyProps.desktop,
desktop_threads: userNotifyProps.desktop_threads || settings.desktop_threads,
desktop_sound: convertDesktopSoundNotifyPropFromUserToDesktop(userNotifyProps.desktop_sound),
desktop_notification_sound: userNotifyProps?.desktop_notification_sound ?? notificationSoundKeys[0] as ChannelNotifyProps['desktop_notification_sound'],
});
}
if (sectionName === 'push') {
setSettings({...settings, push: userNotifyProps.desktop, push_threads: userNotifyProps.push_threads || settings.push_threads});
}
}
const isDesktopSameAsDefault =
userNotifyProps.desktop === settings.desktop &&
userNotifyProps.desktop_threads === settings.desktop_threads &&
userNotifyProps.desktop_notification_sound === settings.desktop_notification_sound &&
convertDesktopSoundNotifyPropFromUserToDesktop(userNotifyProps.desktop_sound) === settings.desktop_sound;
const isPushSameAsDefault = (userNotifyProps.push === settings.push && userNotifyProps.push_threads === settings.push_threads);
if ((sectionName === 'desktop' && isDesktopSameAsDefault) || (sectionName === 'push' && isPushSameAsDefault)) {
return undefined;
}
return (
<button
className='channel-notifications-settings-modal__reset-btn'
onClick={() => resetToDefault(settingName)}
onClick={() => resetToDefault(sectionName)}
data-testid={`resetToDefaultButton-${sectionName}`}
>
<RefreshIcon
size={14}
color={'currentColor'}
/>
{formatMessage({
id: 'channel_notifications.resetToDefault',
defaultMessage: 'Reset to default',
})}
<FormattedMessage
id='channel_notifications.resetToDefault'
defaultMessage='Reset to default'
/>
</button>
);
}, [props.currentUser, settings]);
}, [props.currentUser.notify_props, settings]);
const desktopAndMobileNotificationSectionContent = settings.mark_unread === 'all' ? (
<>
@@ -292,7 +369,7 @@ export default function ChannelNotificationsModal(props: Props) {
id: 'channel_notifications.desktopNotificationsDesc',
defaultMessage: 'Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.',
})}
content={DesktopNotificationsSectionContent}
content={desktopNotificationsSectionContent}
/>
<div className='channel-notifications-settings-modal__divider'/>
<ModalSection
@@ -305,7 +382,7 @@ export default function ChannelNotificationsModal(props: Props) {
id: 'channel_notifications.mobileNotificationsDesc',
defaultMessage: 'Notification alerts are pushed to your mobile device when there is activity in Mattermost.',
})}
content={MobileNotificationsSectionContent}
content={mobileNotificationsSectionContent}
/>
</>
) : (
@@ -359,7 +436,7 @@ export default function ChannelNotificationsModal(props: Props) {
id: 'channel_notifications.muteAndIgnore',
defaultMessage: 'Mute or ignore',
})}
content={MuteOrIgnoreSectionContent}
content={muteOrIgnoreSectionContent}
/>
{desktopAndMobileNotificationSectionContent}
{props.collapsedReplyThreads &&
@@ -374,7 +451,7 @@ export default function ChannelNotificationsModal(props: Props) {
id: 'channel_notifications.autoFollowThreadsDesc',
defaultMessage: 'When enabled, all new replies in this channel will be automatically followed and will appear in your Threads view.',
})}
content={AutoFollowThreadsSectionContent}
content={autoFollowThreadsSectionContent}
/>
</>
}
@@ -407,3 +484,4 @@ export default function ChannelNotificationsModal(props: Props) {
</Modal>
);
}

Просмотреть файл

@@ -9,8 +9,10 @@ import type {UserNotifyProps} from '@mattermost/types/users';
import type {FieldsetCheckbox} from 'components/widgets/modals/components/checkbox_setting_item';
import type {FieldsetRadio} from 'components/widgets/modals/components/radio_setting_item';
import type {FieldsetReactSelect} from 'components/widgets/modals/components/react_select_item';
import {NotificationLevels} from 'utils/constants';
import {DesktopSound, NotificationLevels} from 'utils/constants';
import {optionsOfMessageNotificationSoundsSelect} from 'utils/notification_sounds';
export type ChannelMemberNotifyProps = Partial<ChannelNotifyProps> & Pick<UserNotifyProps, 'desktop_threads' | 'push_threads'>
@@ -102,6 +104,17 @@ export const desktopNotificationInputFieldData = (defaultOption: string): Fields
};
};
export const desktopNotificationSoundsCheckboxFieldData: FieldsetCheckbox = {
name: 'desktopNotificationSoundsCheckbox',
dataTestId: 'desktopNotificationSoundsCheckbox',
};
export const desktopNotificationSoundsSelectFieldData: FieldsetReactSelect = {
id: 'desktopNotificationSoundsSelect',
inputId: 'desktopNotificationSoundsSelectInputId',
options: optionsOfMessageNotificationSoundsSelect,
};
export const mobileNotificationInputFieldData = (defaultOption: string): FieldsetRadio => {
return {
options: [
@@ -160,8 +173,26 @@ export const mobileNotificationInputFieldData = (defaultOption: string): Fieldse
};
};
/**
* This conversion is needed because User's preference for desktop sound is stored as either true or false. On the other hand,
* Channel's specific desktop sound is stored as either On or Off.
*/
export function convertDesktopSoundNotifyPropFromUserToDesktop(userNotifyDesktopSound?: UserNotifyProps['desktop_sound']) {
if (!userNotifyDesktopSound) {
return DesktopSound.OFF;
}
if (userNotifyDesktopSound === 'true') {
return DesktopSound.ON;
}
return DesktopSound.ON;
}
const utils = {
desktopNotificationInputFieldData,
desktopNotificationSoundsCheckboxFieldData,
desktopNotificationSoundsSelectFieldData,
IgnoreMentionsInputFieldData,
mobileNotificationInputFieldData,
MuteChannelInputFieldData,

Просмотреть файл

@@ -15,11 +15,15 @@ import type SettingItemMinComponent from 'components/setting_item_min';
import {UserSettingsNotificationSections} from 'utils/constants';
import {
callsNotificationSounds,
notificationSounds,
notificationSoundKeys,
stopTryNotificationRing,
tryNotificationSound,
tryNotificationRing,
getValueOfNotificationSoundsSelect,
getValueOfIncomingCallSoundsSelect,
optionsOfMessageNotificationSoundsSelect,
optionsOfIncomingCallSoundsSelect,
callNotificationSoundKeys,
} from 'utils/notification_sounds';
import type {Props as UserSettingsNotificationsProps} from '../user_settings_notifications';
@@ -143,7 +147,7 @@ function DesktopNotificationSoundsSettings({
defaultMessage: 'Select a sound',
})}
components={{IndicatorSeparator: NoIndicatorSeparatorComponent}}
value={getValueOfMessageNotificationSoundsSelect(desktopNotificationSound)}
value={getValueOfNotificationSoundsSelect(desktopNotificationSound)}
onChange={handleChangeForMessageNotificationSoundSelect}
/>
</div>
@@ -259,146 +263,6 @@ function NoIndicatorSeparatorComponent() {
return null;
}
const notificationSoundKeys = Array.from(notificationSounds.keys());
const optionsOfMessageNotificationSoundsSelect: SelectOption[] = notificationSoundKeys.map((soundName) => {
if (soundName === 'Bing') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundBing'
defaultMessage='Bing'
/>
),
};
} else if (soundName === 'Crackle') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundCrackle'
defaultMessage='Crackle'
/>
),
};
} else if (soundName === 'Down') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundDown'
defaultMessage='Down'
/>
),
};
} else if (soundName === 'Hello') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundHello'
defaultMessage='Hello'
/>
),
};
} else if (soundName === 'Ripple') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundRipple'
defaultMessage='Ripple'
/>
),
};
} else if (soundName === 'Upstairs') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundUpstairs'
defaultMessage='Upstairs'
/>
),
};
}
return {
value: '',
label: '',
};
});
function getValueOfMessageNotificationSoundsSelect(soundName?: string) {
const soundOption = optionsOfMessageNotificationSoundsSelect.find((option) => option.value === soundName);
if (!soundOption) {
return undefined;
}
return soundOption;
}
const callNotificationSoundKeys = Array.from(callsNotificationSounds.keys());
const optionsOfIncomingCallSoundsSelect: SelectOption[] = callNotificationSoundKeys.map((soundName) => {
if (soundName === 'Dynamic') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundDynamic'
defaultMessage='Dynamic'
/>
),
};
} else if (soundName === 'Calm') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundCalm'
defaultMessage='Calm'
/>
),
};
} else if (soundName === 'Urgent') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundUrgent'
defaultMessage='Urgent'
/>
),
};
} else if (soundName === 'Cheerful') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundCheerful'
defaultMessage='Cheerful'
/>
),
};
}
return {
value: '',
label: '',
};
});
function getValueOfIncomingCallSoundsSelect(soundName?: string) {
const soundOption = optionsOfIncomingCallSoundsSelect.find((option) => option.value === soundName);
if (!soundOption) {
return undefined;
}
return soundOption;
}
function getCollapsedText(
isCallsRingingEnabled: UserSettingsNotificationsProps['isCallsRingingEnabled'],
desktopSound: UserNotifyProps['desktop_sound'],

Просмотреть файл

@@ -20,7 +20,7 @@ import SettingItemMax from 'components/setting_item_max';
import RestrictedIndicator from 'components/widgets/menu/menu_items/restricted_indicator';
import Constants, {NotificationLevels, MattermostFeatures, LicenseSkus, UserSettingsNotificationSections} from 'utils/constants';
import {stopTryNotificationRing} from 'utils/notification_sounds';
import {notificationSoundKeys, stopTryNotificationRing} from 'utils/notification_sounds';
import {a11yFocus} from 'utils/utils';
import DesktopAndMobileNotificationSettings from './desktop_and_mobile_notification_setting';
@@ -88,7 +88,7 @@ function getDefaultStateFromProps(props: Props): State {
let emailThreads: UserNotifyProps['email_threads'] = NotificationLevels.ALL;
let sound: UserNotifyProps['desktop_sound'] = 'true';
let callsSound: UserNotifyProps['calls_desktop_sound'] = 'true';
let desktopNotificationSound: UserNotifyProps['desktop_notification_sound'] = 'Bing';
let desktopNotificationSound: UserNotifyProps['desktop_notification_sound'] = notificationSoundKeys[0] as UserNotifyProps['desktop_notification_sound'];
let callsNotificationSound: UserNotifyProps['calls_notification_sound'] = 'Calm';
let comments: UserNotifyProps['comments'] = 'never';
let enableEmail: UserNotifyProps['email'] = 'true';

Просмотреть файл

@@ -50,6 +50,11 @@
&__content {
display: flex;
flex-direction: column;
&.inline {
flex-direction: row;
gap: 24px;
}
}
&__fieldset-checkbox-ctr {
@@ -90,9 +95,10 @@
line-height: 20px;
}
// Todo: add styling for react select
//&__fieldset-react-select{
//}
&__fieldset-react-select {
max-width: 240px;
flex-grow: 1;
}
&__input-checkbox {
width: 1.6rem;

Просмотреть файл

@@ -23,11 +23,12 @@ export type BaseSettingItemProps = {
type Props = BaseSettingItemProps & {
content: JSX.Element;
isContentInline?: boolean;
className?: string;
descriptionAboveContent?: boolean;
}
function BaseSettingItem({title, description, content, className, error, descriptionAboveContent = false}: Props): JSX.Element {
function BaseSettingItem({title, description, content, className, error, descriptionAboveContent = false, isContentInline = false}: Props): JSX.Element {
const {formatMessage} = useIntl();
const titleComponent = title && (
@@ -64,7 +65,9 @@ function BaseSettingItem({title, description, content, className, error, descrip
{descriptionAboveContent ? descriptionComponent : undefined}
<div
data-testid='mm-modal-generic-section-item__content'
className='mm-modal-generic-section-item__content'
className={classNames('mm-modal-generic-section-item__content', {
inline: isContentInline,
})}
>
{content}
</div>

Просмотреть файл

@@ -0,0 +1,101 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {ReactNode} from 'react';
import React from 'react';
import ReactSelect from 'react-select';
import type {ValueType} from 'react-select';
import type {BaseSettingItemProps} from './base_setting_item';
import BaseSettingItem from './base_setting_item';
import type {FieldsetCheckbox} from './checkbox_setting_item';
import type {FieldsetReactSelect, Option} from './react_select_item';
type Props = BaseSettingItemProps & {
containerClassName?: string;
descriptionAboveContent?: boolean;
checkboxFieldTitle: ReactNode;
checkboxFieldData: FieldsetCheckbox;
checkboxFieldValue: boolean;
handleCheckboxChange: (e: boolean) => void;
selectFieldData: FieldsetReactSelect;
selectFieldValue?: Option;
handleSelectChange: (selected: ValueType<Option>) => void;
isSelectDisabled?: boolean;
selectPlaceholder?: string;
}
export default function CheckboxWithSelectSettingItem({
title,
description,
containerClassName,
descriptionAboveContent = false,
checkboxFieldTitle,
checkboxFieldData,
checkboxFieldValue,
handleCheckboxChange,
selectFieldData,
selectFieldValue,
handleSelectChange,
isSelectDisabled,
selectPlaceholder,
}: Props) {
const content = (
<>
<fieldset
key={checkboxFieldData.name}
className='mm-modal-generic-section-item__fieldset-checkbox-ctr'
>
<label className='mm-modal-generic-section-item__fieldset-checkbox'>
<input
className='mm-modal-generic-section-item__input-checkbox'
data-testid={checkboxFieldData.dataTestId}
type='checkbox'
name={checkboxFieldData.name}
checked={checkboxFieldValue}
onChange={(e) => handleCheckboxChange(e.target.checked)}
/>
{checkboxFieldTitle}
</label>
</fieldset>
<fieldset className='mm-modal-generic-section-item__fieldset-react-select'>
<ReactSelect
id={selectFieldData.id}
inputId={selectFieldData.inputId}
aria-labelledby={selectFieldData.ariaLabelledby}
className='react-select singleSelect react-select-top'
classNamePrefix='react-select'
options={selectFieldData.options}
clearable={selectFieldData.clearable}
isClearable={selectFieldData.clearable}
isDisabled={isSelectDisabled}
isSearchable={false}
placeholder={selectPlaceholder}
onChange={(value) => handleSelectChange(value)}
value={selectFieldValue}
components={{IndicatorSeparator: NoIndicatorSeparatorComponent}}
/>
</fieldset>
</>
);
return (
<BaseSettingItem
title={title}
content={content}
isContentInline={true}
description={description}
className={containerClassName}
descriptionAboveContent={descriptionAboveContent}
/>
);
}
function NoIndicatorSeparatorComponent() {
return null;
}
export type SelectOption = {
value: string;
label: ReactNode;
};

Просмотреть файл

@@ -1,6 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {ReactNode} from 'react';
import React from 'react';
import type {ValueType} from 'react-select';
import ReactSelect from 'react-select';
@@ -9,12 +10,17 @@ import type {BaseSettingItemProps} from './base_setting_item';
import BaseSettingItem from './base_setting_item';
export type Option = {
value: number;
label: string;
value: string;
label: ReactNode;
};
export type FieldsetReactSelect = {
id: string;
name?: string;
inputId?: string;
dataTestId?: string;
ariaLabelledby?: string;
clearable?: boolean;
options: Option[];
}
@@ -23,6 +29,7 @@ type Props = BaseSettingItemProps & {
inputFieldValue: Option;
handleChange: (selected: ValueType<Option>) => void;
}
function ReactSelectItemCreator({
title,
description,
@@ -33,19 +40,23 @@ function ReactSelectItemCreator({
const content = (
<fieldset className='mm-modal-generic-section-item__fieldset-react-select'>
<ReactSelect
className='react-select'
id={inputFieldData.id}
name={inputFieldData.name}
inputId={inputFieldData.inputId}
aria-labelledby={inputFieldData.ariaLabelledby}
className='react-select singleSelect react-select-top'
classNamePrefix='react-select'
id='limitVisibleGMsDMs'
options={inputFieldData.options}
clearable={false}
clearable={inputFieldData.clearable}
isClearable={inputFieldData.clearable}
isSearchable={false}
onChange={handleChange}
value={inputFieldValue}
isSearchable={false}
menuPortalTarget={document.body}
styles={reactStyles}
components={{IndicatorSeparator: NoIndicatorSeparatorComponent}}
/>
</fieldset>
);
return (
<BaseSettingItem
content={content}
@@ -57,9 +68,6 @@ function ReactSelectItemCreator({
export default ReactSelectItemCreator;
const reactStyles = {
menuPortal: (provided: React.CSSProperties) => ({
...provided,
zIndex: 9999,
}),
};
function NoIndicatorSeparatorComponent() {
return null;
}

Просмотреть файл

@@ -3202,6 +3202,9 @@
"channel_notifications.desktopNotificationAllLabel": "All new messages",
"channel_notifications.desktopNotificationMentionLabel": "Mentions, direct messages, and keywords only",
"channel_notifications.desktopNotificationNothingLabel": "Nothing",
"channel_notifications.desktopNotifications.soundEnable": "Message notification sounds",
"channel_notifications.desktopNotifications.soundSelectPlaceholder": "Select a sound",
"channel_notifications.desktopNotifications.title": "Sounds",
"channel_notifications.desktopNotificationsDesc": "Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.",
"channel_notifications.desktopNotificationsTitle": "Desktop Notifications",
"channel_notifications.ignoreMentionsDesc": "When enabled, @channel, @here and @all will not trigger mentions or mention notifications in this channel",

Просмотреть файл

@@ -1,107 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import bing from 'sounds/bing.mp3';
import calls_calm from 'sounds/calls_calm.mp3';
import calls_cheerful from 'sounds/calls_cheerful.mp3';
import calls_dynamic from 'sounds/calls_dynamic.mp3';
import calls_urgent from 'sounds/calls_urgent.mp3';
import crackle from 'sounds/crackle.mp3';
import down from 'sounds/down.mp3';
import hello from 'sounds/hello.mp3';
import ripple from 'sounds/ripple.mp3';
import upstairs from 'sounds/upstairs.mp3';
import * as UserAgent from 'utils/user_agent';
export const notificationSounds = new Map([
['Bing', bing],
['Crackle', crackle],
['Down', down],
['Hello', hello],
['Ripple', ripple],
['Upstairs', upstairs],
]);
export const callsNotificationSounds = new Map([
['Dynamic', calls_dynamic],
['Calm', calls_calm],
['Urgent', calls_urgent],
['Cheerful', calls_cheerful],
]);
let canDing = true;
export function ding(name: string) {
if (hasSoundOptions() && canDing) {
tryNotificationSound(name);
canDing = false;
setTimeout(() => {
canDing = true;
}, 3000);
}
}
export function tryNotificationSound(name: string) {
const audio = new Audio(notificationSounds.get(name) ?? notificationSounds.get('Bing'));
audio.play();
}
let currentRing: HTMLAudioElement | null = null;
export function ring(name: string) {
if (!hasSoundOptions()) {
return;
}
stopRing();
currentRing = loopNotificationRing(name);
currentRing.addEventListener('pause', () => {
stopRing();
});
}
export function stopRing() {
if (currentRing) {
currentRing.pause();
currentRing.src = '';
currentRing.remove();
currentRing = null;
}
}
let currentTryRing: HTMLAudioElement | null = null;
let currentTimer: NodeJS.Timeout;
export function tryNotificationRing(name: string) {
if (!hasSoundOptions()) {
return;
}
stopTryNotificationRing();
clearTimeout(currentTimer);
currentTryRing = loopNotificationRing(name);
currentTryRing.addEventListener('pause', () => {
stopTryNotificationRing();
});
currentTimer = setTimeout(() => {
stopTryNotificationRing();
}, 5000);
}
export function stopTryNotificationRing() {
if (currentTryRing) {
currentTryRing.pause();
currentTryRing.src = '';
currentTryRing.remove();
currentTryRing = null;
}
}
export function loopNotificationRing(name: string) {
const audio = new Audio(callsNotificationSounds.get(name) ?? callsNotificationSounds.get('Calm'));
audio.loop = true;
audio.play();
return audio;
}
export function hasSoundOptions() {
return (!UserAgent.isEdge());
}

Просмотреть файл

@@ -0,0 +1,251 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {ReactNode} from 'react';
import React from 'react';
import {FormattedMessage} from 'react-intl';
import bing from 'sounds/bing.mp3';
import calls_calm from 'sounds/calls_calm.mp3';
import calls_cheerful from 'sounds/calls_cheerful.mp3';
import calls_dynamic from 'sounds/calls_dynamic.mp3';
import calls_urgent from 'sounds/calls_urgent.mp3';
import crackle from 'sounds/crackle.mp3';
import down from 'sounds/down.mp3';
import hello from 'sounds/hello.mp3';
import ripple from 'sounds/ripple.mp3';
import upstairs from 'sounds/upstairs.mp3';
import * as UserAgent from 'utils/user_agent';
export const notificationSounds = new Map([
['Bing', bing],
['Crackle', crackle],
['Down', down],
['Hello', hello],
['Ripple', ripple],
['Upstairs', upstairs],
]);
export const notificationSoundKeys = Array.from(notificationSounds.keys());
export const optionsOfMessageNotificationSoundsSelect: Array<{value: string; label: ReactNode}> = notificationSoundKeys.map((soundName) => {
if (soundName === 'Bing') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundBing'
defaultMessage='Bing'
/>
),
};
} else if (soundName === 'Crackle') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundCrackle'
defaultMessage='Crackle'
/>
),
};
} else if (soundName === 'Down') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundDown'
defaultMessage='Down'
/>
),
};
} else if (soundName === 'Hello') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundHello'
defaultMessage='Hello'
/>
),
};
} else if (soundName === 'Ripple') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundRipple'
defaultMessage='Ripple'
/>
),
};
} else if (soundName === 'Upstairs') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundUpstairs'
defaultMessage='Upstairs'
/>
),
};
}
return {
value: '',
label: '',
};
});
export function getValueOfNotificationSoundsSelect(soundName?: string) {
const soundOption = optionsOfMessageNotificationSoundsSelect.find((option) => option.value === soundName);
if (!soundOption) {
return undefined;
}
return soundOption;
}
export const callsNotificationSounds = new Map([
['Dynamic', calls_dynamic],
['Calm', calls_calm],
['Urgent', calls_urgent],
['Cheerful', calls_cheerful],
]);
export const callNotificationSoundKeys = Array.from(callsNotificationSounds.keys());
export const optionsOfIncomingCallSoundsSelect: Array<{value: string; label: ReactNode}> = callNotificationSoundKeys.map((soundName) => {
if (soundName === 'Dynamic') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundDynamic'
defaultMessage='Dynamic'
/>
),
};
} else if (soundName === 'Calm') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundCalm'
defaultMessage='Calm'
/>
),
};
} else if (soundName === 'Urgent') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundUrgent'
defaultMessage='Urgent'
/>
),
};
} else if (soundName === 'Cheerful') {
return {
value: soundName,
label: (
<FormattedMessage
id='user.settings.notifications.desktopNotificationSound.soundCheerful'
defaultMessage='Cheerful'
/>
),
};
}
return {
value: '',
label: '',
};
});
export function getValueOfIncomingCallSoundsSelect(soundName?: string) {
const soundOption = optionsOfIncomingCallSoundsSelect.find((option) => option.value === soundName);
if (!soundOption) {
return undefined;
}
return soundOption;
}
let canDing = true;
export function ding(name: string) {
if (hasSoundOptions() && canDing) {
tryNotificationSound(name);
canDing = false;
setTimeout(() => {
canDing = true;
}, 3000);
}
}
export function tryNotificationSound(name: string) {
const audio = new Audio(notificationSounds.get(name) ?? notificationSounds.get('Bing'));
audio.play();
}
let currentRing: HTMLAudioElement | null = null;
export function ring(name: string) {
if (!hasSoundOptions()) {
return;
}
stopRing();
currentRing = loopNotificationRing(name);
currentRing.addEventListener('pause', () => {
stopRing();
});
}
export function stopRing() {
if (currentRing) {
currentRing.pause();
currentRing.src = '';
currentRing.remove();
currentRing = null;
}
}
let currentTryRing: HTMLAudioElement | null = null;
let currentTimer: NodeJS.Timeout;
export function tryNotificationRing(name: string) {
if (!hasSoundOptions()) {
return;
}
stopTryNotificationRing();
clearTimeout(currentTimer);
currentTryRing = loopNotificationRing(name);
currentTryRing.addEventListener('pause', () => {
stopTryNotificationRing();
});
currentTimer = setTimeout(() => {
stopTryNotificationRing();
}, 5000);
}
export function stopTryNotificationRing() {
if (currentTryRing) {
currentTryRing.pause();
currentTryRing.src = '';
currentTryRing.remove();
currentTryRing = null;
}
}
export function loopNotificationRing(name: string) {
const audio = new Audio(callsNotificationSounds.get(name) ?? callsNotificationSounds.get('Calm'));
audio.loop = true;
audio.play();
return audio;
}
export function hasSoundOptions() {
return (!UserAgent.isEdge());
}