Skip to content

Commit

Permalink
feat: User sound preferences master volume and ringing volume (#33902)
Browse files Browse the repository at this point in the history
  • Loading branch information
aleksandernsilva authored Nov 19, 2024
1 parent ef0c5eb commit d1e6a73
Show file tree
Hide file tree
Showing 71 changed files with 179 additions and 104 deletions.
6 changes: 6 additions & 0 deletions .changeset/seven-berries-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/i18n": patch
---

Adds "Master volume" and "Call ringer volume" to the user preferences sound section.
3 changes: 2 additions & 1 deletion apps/meteor/app/ui/client/lib/KonchatNotification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { e2e } from '../../../e2e/client';
import { Subscriptions } from '../../../models/client';
import { getUserPreference } from '../../../utils/client';
import { getUserAvatarURL } from '../../../utils/client/getUserAvatarURL';
import { getUserNotificationsSoundVolume } from '../../../utils/client/getUserNotificationsSoundVolume';
import { sdk } from '../../../utils/client/lib/SDKClient';

declare global {
Expand Down Expand Up @@ -176,7 +177,7 @@ class KonchatNotification {

const userId = Meteor.userId();
const newMessageNotification = getUserPreference<string>(userId, 'newMessageNotification');
const audioVolume = getUserPreference(userId, 'notificationsSoundVolume', 100);
const audioVolume = getUserNotificationsSoundVolume(userId);

if (!rid) {
return;
Expand Down
10 changes: 10 additions & 0 deletions apps/meteor/app/utils/client/getUserNotificationsSoundVolume.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { IUser } from '@rocket.chat/core-typings';

import { getUserPreference } from './lib/getUserPreference';

export const getUserNotificationsSoundVolume = (userId: IUser['_id'] | null | undefined) => {
const masterVolume = getUserPreference<number>(userId, 'masterVolume', 100);
const notificationsSoundVolume = getUserPreference<number>(userId, 'notificationsSoundVolume', 100);

return (notificationsSoundVolume * masterVolume) / 100;
};
9 changes: 4 additions & 5 deletions apps/meteor/client/hooks/useContinuousSoundNotification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ICustomSound } from '@rocket.chat/core-typings';
import { useSetting, useUserPreference, useUserSubscriptions } from '@rocket.chat/ui-contexts';
import { useEffect } from 'react';

import { useUserSoundPreferences } from './useUserSoundPreferences';
import { CustomSounds } from '../../app/custom-sounds/client/lib/CustomSounds';

const query = { t: 'l', ls: { $exists: false }, open: true };
Expand All @@ -11,12 +12,10 @@ export const useContinuousSoundNotification = () => {
const playNewRoomSoundContinuously = useSetting('Livechat_continuous_sound_notification_new_livechat_room');

const newRoomNotification = useUserPreference<string>('newRoomNotification');
const audioVolume = useUserPreference<number>('notificationsSoundVolume');
const { notificationsSoundVolume } = useUserSoundPreferences();

const continuousCustomSoundId = newRoomNotification && `${newRoomNotification}-continuous`;

const volume = audioVolume !== undefined ? Number((audioVolume / 100).toPrecision(2)) : 1;

useEffect(() => {
let audio: ICustomSound;
if (playNewRoomSoundContinuously && continuousCustomSoundId) {
Expand Down Expand Up @@ -46,8 +45,8 @@ export const useContinuousSoundNotification = () => {
}

CustomSounds.play(continuousCustomSoundId, {
volume,
volume: notificationsSoundVolume,
loop: true,
});
}, [continuousCustomSoundId, playNewRoomSoundContinuously, userSubscriptions, volume]);
}, [continuousCustomSoundId, playNewRoomSoundContinuously, userSubscriptions, notificationsSoundVolume]);
};
17 changes: 17 additions & 0 deletions apps/meteor/client/hooks/useUserSoundPreferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useUserPreference } from '@rocket.chat/ui-contexts';

const relativeVolume = (volume: number, masterVolume: number) => {
return (volume * masterVolume) / 100;
};

export const useUserSoundPreferences = () => {
const masterVolume = useUserPreference<number>('masterVolume', 100) || 100;
const notificationsSoundVolume = useUserPreference<number>('notificationsSoundVolume', 100) || 100;
const voipRingerVolume = useUserPreference<number>('voipRingerVolume', 100) || 100;

return {
masterVolume,
notificationsSoundVolume: relativeVolume(notificationsSoundVolume, masterVolume),
voipRingerVolume: relativeVolume(voipRingerVolume, masterVolume),
};
};
10 changes: 6 additions & 4 deletions apps/meteor/client/providers/CallProvider/hooks/useVoipSounds.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { useCustomSound, useUserPreference } from '@rocket.chat/ui-contexts';
import { useCustomSound } from '@rocket.chat/ui-contexts';
import { useMemo } from 'react';

import { useUserSoundPreferences } from '../../../hooks/useUserSoundPreferences';

type VoipSound = 'telephone' | 'outbound-call-ringing' | 'call-ended';

export const useVoipSounds = () => {
const { play, pause } = useCustomSound();
const audioVolume = useUserPreference<number>('notificationsSoundVolume', 100) || 100;
const { voipRingerVolume } = useUserSoundPreferences();

return useMemo(
() => ({
play: (soundId: VoipSound, loop = true) => {
play(soundId, {
volume: Number((audioVolume / 100).toPrecision(2)),
volume: Number((voipRingerVolume / 100).toPrecision(2)),
loop,
});
},
Expand All @@ -21,6 +23,6 @@ export const useVoipSounds = () => {
pause('outbound-call-ringing');
},
}),
[play, pause, audioVolume],
[play, pause, voipRingerVolume],
);
};
3 changes: 2 additions & 1 deletion apps/meteor/client/startup/notifications/notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Tracker } from 'meteor/tracker';
import { CustomSounds } from '../../../app/custom-sounds/client/lib/CustomSounds';
import { Users } from '../../../app/models/client';
import { getUserPreference } from '../../../app/utils/client';
import { getUserNotificationsSoundVolume } from '../../../app/utils/client/getUserNotificationsSoundVolume';

Meteor.startup(() => {
Tracker.autorun(() => {
Expand All @@ -21,7 +22,7 @@ Meteor.startup(() => {
},
});
const newRoomNotification = getUserPreference<string>(user, 'newRoomNotification');
const audioVolume = getUserPreference(user, 'notificationsSoundVolume', 100);
const audioVolume = getUserNotificationsSoundVolume(user?._id);

if (!newRoomNotification) {
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { SelectOption } from '@rocket.chat/fuselage';
import { Accordion, Field, FieldLabel, FieldRow, Select, FieldGroup, ToggleSwitch, Tooltip, Box } from '@rocket.chat/fuselage';
import { Accordion, Field, FieldLabel, FieldRow, Select, FieldGroup, ToggleSwitch, FieldHint, Slider } from '@rocket.chat/fuselage';
import { useUniqueId } from '@rocket.chat/fuselage-hooks';
import { useTranslation, useCustomSound } from '@rocket.chat/ui-contexts';
import type { ChangeEvent } from 'react';
import React from 'react';
import { Controller, useFormContext } from 'react-hook-form';

Expand All @@ -12,16 +11,92 @@ const PreferencesSoundSection = () => {
const customSound = useCustomSound();
const soundsList: SelectOption[] = customSound?.getList()?.map((value) => [value._id, value.name]) || [];
const { control, watch } = useFormContext();
const { newMessageNotification, notificationsSoundVolume } = watch();
const { newMessageNotification, notificationsSoundVolume = 100, masterVolume = 100, voipRingerVolume = 100 } = watch();

const newRoomNotificationId = useUniqueId();
const newMessageNotificationId = useUniqueId();
const muteFocusedConversationsId = useUniqueId();
const masterVolumeId = useUniqueId();
const notificationsSoundVolumeId = useUniqueId();
const voipRingerVolumeId = useUniqueId();

return (
<Accordion.Item title={t('Sound')}>
<FieldGroup>
<Field>
<FieldLabel aria-describedby={`${masterVolumeId}-hint`}>{t('Master_volume')}</FieldLabel>
<FieldHint id={`${masterVolumeId}-hint`} mbe={4}>
{t('Master_volume_hint')}
</FieldHint>
<FieldRow>
<Controller
name='masterVolume'
control={control}
render={({ field: { onChange, value } }) => (
<Slider
aria-labelledby={masterVolumeId}
aria-describedby={`${masterVolumeId}-hint`}
value={value}
minValue={0}
maxValue={100}
onChange={onChange}
/>
)}
/>
</FieldRow>
</Field>
<Field>
<FieldLabel id={notificationsSoundVolumeId}>{t('Notification_volume')}</FieldLabel>
<FieldHint id={`${notificationsSoundVolumeId}-hint`} mbe={4}>
{t('Notification_volume_hint')}
</FieldHint>
<FieldRow>
<Controller
name='notificationsSoundVolume'
control={control}
render={({ field: { onChange, value } }) => (
<Slider
aria-labelledby={notificationsSoundVolumeId}
aria-describedby={`${notificationsSoundVolumeId}-hint`}
value={value}
minValue={0}
maxValue={100}
onChange={(value: number) => {
const soundVolume = (notificationsSoundVolume * masterVolume) / 100;
customSound.play(newMessageNotification, { volume: soundVolume / 100 });
onChange(value);
}}
/>
)}
/>
</FieldRow>
</Field>
<Field>
<FieldLabel aria-describedby={`${voipRingerVolumeId}-hint`}>{t('Call_ringer_volume')}</FieldLabel>
<FieldHint id={`${voipRingerVolumeId}-hint`} mbe={4}>
{t('Call_ringer_volume_hint')}
</FieldHint>
<FieldRow>
<Controller
name='voipRingerVolume'
control={control}
render={({ field: { onChange, value } }) => (
<Slider
aria-labelledby={voipRingerVolumeId}
aria-describedby={`${voipRingerVolumeId}-hint`}
value={value}
minValue={0}
maxValue={100}
onChange={(value: number) => {
const soundVolume = (voipRingerVolume * masterVolume) / 100;
customSound.play('telephone', { volume: soundVolume / 100 });
onChange(value);
}}
/>
)}
/>
</FieldRow>
</Field>
<Field>
<FieldLabel htmlFor={newRoomNotificationId}>{t('New_Room_Notification')}</FieldLabel>
<FieldRow>
Expand All @@ -32,11 +107,11 @@ const PreferencesSoundSection = () => {
<Select
id={newRoomNotificationId}
value={value}
options={soundsList}
onChange={(value) => {
onChange(value);
customSound.play(String(value), { volume: notificationsSoundVolume / 100 });
}}
options={soundsList}
/>
)}
/>
Expand All @@ -52,11 +127,11 @@ const PreferencesSoundSection = () => {
<Select
id={newMessageNotificationId}
value={value}
options={soundsList}
onChange={(value) => {
onChange(value);
customSound.play(String(value), { volume: notificationsSoundVolume / 100 });
}}
options={soundsList}
/>
)}
/>
Expand All @@ -74,34 +149,6 @@ const PreferencesSoundSection = () => {
/>
</FieldRow>
</Field>
<Field>
<FieldLabel htmlFor={notificationsSoundVolumeId}>{t('Notifications_Sound_Volume')}</FieldLabel>
<FieldRow>
<Controller
name='notificationsSoundVolume'
control={control}
render={({ field: { onChange, value, ref } }) => (
<Box
id={notificationsSoundVolumeId}
ref={ref}
is='input'
flexGrow={1}
type='range'
min='0'
max='100'
value={value}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
customSound.play(newMessageNotification, { volume: notificationsSoundVolume / 100 });
onChange(Math.max(0, Math.min(Number(e.currentTarget.value), 100)));
}}
/>
)}
/>
<Tooltip placement='right' mis={8}>
{notificationsSoundVolume}
</Tooltip>
</FieldRow>
</Field>
</FieldGroup>
</Accordion.Item>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ export type AccountPreferencesData = {
sidebarViewMode?: string;
sidebarDisplayAvatar?: boolean;
sidebarGroupByType?: boolean;
masterVolume?: number;
notificationsSoundVolume?: number;
voipRingerVolume?: number;
};

export const useAccountPreferencesValues = (): AccountPreferencesData => {
Expand Down Expand Up @@ -70,7 +72,10 @@ export const useAccountPreferencesValues = (): AccountPreferencesData => {
const newRoomNotification = useUserPreference<string>('newRoomNotification');
const newMessageNotification = useUserPreference<string>('newMessageNotification');
const muteFocusedConversations = useUserPreference<boolean>('muteFocusedConversations');
const notificationsSoundVolume = useUserPreference<number>('notificationsSoundVolume');

const masterVolume = useUserPreference<number>('masterVolume', 100);
const notificationsSoundVolume = useUserPreference<number>('notificationsSoundVolume', 100);
const voipRingerVolume = useUserPreference<number>('voipRingerVolume', 100);

return {
language,
Expand Down Expand Up @@ -99,6 +104,8 @@ export const useAccountPreferencesValues = (): AccountPreferencesData => {
newRoomNotification,
newMessageNotification,
muteFocusedConversations,
masterVolume,
notificationsSoundVolume,
voipRingerVolume,
};
};
4 changes: 4 additions & 0 deletions apps/meteor/server/methods/saveUserPreferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ type UserPreferences = {
autoImageLoad: boolean;
emailNotificationMode: string;
unreadAlert: boolean;
masterVolume: number;
notificationsSoundVolume: number;
voipRingerVolume: number;
desktopNotifications: string;
pushNotifications: string;
enableAutoAway: boolean;
Expand Down Expand Up @@ -97,7 +99,9 @@ export const saveUserPreferences = async (settings: Partial<UserPreferences>, us
autoImageLoad: Match.Optional(Boolean),
emailNotificationMode: Match.Optional(String),
unreadAlert: Match.Optional(Boolean),
masterVolume: Match.Optional(Number),
notificationsSoundVolume: Match.Optional(Number),
voipRingerVolume: Match.Optional(Number),
desktopNotifications: Match.Optional(String),
pushNotifications: Match.Optional(String),
enableAutoAway: Match.Optional(Boolean),
Expand Down
14 changes: 13 additions & 1 deletion apps/meteor/server/settings/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -698,10 +698,22 @@ export const createAccountSettings = () =>
i18nLabel: 'Mute_Focused_Conversations',
});

await this.add('Accounts_Default_User_Preferences_masterVolume', 100, {
type: 'int',
public: true,
i18nLabel: 'Master_volume',
});

await this.add('Accounts_Default_User_Preferences_notificationsSoundVolume', 100, {
type: 'int',
public: true,
i18nLabel: 'Notifications_Sound_Volume',
i18nLabel: 'Notification_volume',
});

await this.add('Accounts_Default_User_Preferences_voipRingerVolume', 100, {
type: 'int',
public: true,
i18nLabel: 'Call_ringer_volume',
});

await this.add('Accounts_Default_User_Preferences_omnichannelTranscriptEmail', false, {
Expand Down
2 changes: 2 additions & 0 deletions apps/meteor/tests/end-to-end/api/miscellaneous.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ describe('miscellaneous', () => {
'autoImageLoad',
'emailNotificationMode',
'unreadAlert',
'masterVolume',
'notificationsSoundVolume',
'voipRingerVolume',
'omnichannelTranscriptEmail',
IS_EE ? 'omnichannelTranscriptPDF' : false,
'desktopNotifications',
Expand Down
1 change: 0 additions & 1 deletion packages/i18n/src/locales/af.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1805,7 +1805,6 @@
"Notifications_Max_Room_Members_Description": "Maksimum aantal lede in die kamer wanneer kennisgewings vir alle boodskappe afgeskakel word. Gebruikers kan steeds per kamerinstelling verander om alle kennisgewings op individuele basis te ontvang. (0 om uit te skakel)",
"Notifications_Muted_Description": "As jy kies om alles te demper, sal jy nie die kamerhoogte in die lys sien as daar nuwe boodskappe is nie, behalwe vir verwysings. Muting-kennisgewings sal kennisgewingsinstellings ignoreer.",
"Notifications_Preferences": "Kennisgewings Voorkeure",
"Notifications_Sound_Volume": "Kennisgewings klankvolume",
"Notify_active_in_this_room": "Stel aktiewe gebruikers in hierdie kamer in kennis",
"Notify_all_in_this_room": "Stel alles in hierdie kamer in kennis",
"Num_Agents": "# Agente",
Expand Down
1 change: 0 additions & 1 deletion packages/i18n/src/locales/ar.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -3116,7 +3116,6 @@
"Notifications_Max_Room_Members_Description": "الحد الأقصى لعدد الأعضاء في الغرفة عند تعطيل إشعارات كل الرسائل. لا يزال بإمكان المستخدمين تغيير إعداد كل غرفة لتلقي كل الإشعارات على أساس فردي. (0 للتعطيل)",
"Notifications_Muted_Description": "إذا اخترت كتم صوت كل شيء، فلن ترى تمييز الغرفة في القائمة عند وجود رسائل جديدة، باستثناء الإشارات. سيتجاوز كتم صوت الإشعارات إعدادات الإشعارات.",
"Notifications_Preferences": "تفضيلات الإشعارات",
"Notifications_Sound_Volume": "حجم صوت الإشعارات",
"Notify_active_in_this_room": "إخطار المستخدمين النشطين في هذه الغرفة",
"Notify_all_in_this_room": "إخطار جميع من في هذه الغرفة",
"NPS_survey_enabled": "تمكين استطلاع صافي نقاط الترويج",
Expand Down
Loading

0 comments on commit d1e6a73

Please sign in to comment.