diff --git a/.changeset/cold-spiders-act.md b/.changeset/cold-spiders-act.md new file mode 100644 index 0000000000000..6011adc699990 --- /dev/null +++ b/.changeset/cold-spiders-act.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes an issue where custom room notification sounds were not applied. diff --git a/apps/meteor/client/hooks/notification/useNewMessageNotification.ts b/apps/meteor/client/hooks/notification/useNewMessageNotification.ts index 36b94568a27a5..8f1442a5424c6 100644 --- a/apps/meteor/client/hooks/notification/useNewMessageNotification.ts +++ b/apps/meteor/client/hooks/notification/useNewMessageNotification.ts @@ -2,6 +2,8 @@ import type { AtLeast, ISubscription } from '@rocket.chat/core-typings'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useCustomSound } from '@rocket.chat/ui-contexts'; +import { Subscriptions } from '../../stores'; + export const useNewMessageNotification = () => { const { notificationSounds } = useCustomSound(); @@ -9,14 +11,12 @@ export const useNewMessageNotification = () => { if (!sub || sub.audioNotificationValue === 'none') { return; } - // TODO: Fix this - Room Notifications Preferences > sound > desktop is not working. - // plays the user notificationSound preference - // if (sub.audioNotificationValue && sub.audioNotificationValue !== '0') { - // void CustomSounds.play(sub.audioNotificationValue, { - // volume: Number((notificationsSoundVolume / 100).toPrecision(2)), - // }); - // } + const subscription = Subscriptions.state.find((record) => record.rid === sub.rid); + + if (subscription?.audioNotificationValue) { + return notificationSounds.playNewMessageCustom(subscription.audioNotificationValue); + } notificationSounds.playNewMessage(); }); diff --git a/apps/meteor/client/providers/CustomSoundProvider/CustomSoundProvider.tsx b/apps/meteor/client/providers/CustomSoundProvider/CustomSoundProvider.tsx index 82c12f4febb83..e06dd7a354a19 100644 --- a/apps/meteor/client/providers/CustomSoundProvider/CustomSoundProvider.tsx +++ b/apps/meteor/client/providers/CustomSoundProvider/CustomSoundProvider.tsx @@ -76,6 +76,8 @@ const CustomSoundProvider = ({ children }: CustomSoundProviderProps) => { const notificationSounds = { playNewRoom: () => play(newRoomNotification, { loop: false, volume: formatVolume(notificationsSoundVolume) }), playNewMessage: () => play(newMessageNotification, { loop: false, volume: formatVolume(notificationsSoundVolume) }), + playNewMessageCustom: (soundId: ICustomSound['_id']) => + play(soundId, { loop: false, volume: formatVolume(notificationsSoundVolume) }), playNewMessageLoop: () => play(newMessageNotification, { loop: true, volume: formatVolume(notificationsSoundVolume) }), stopNewRoom: () => stop(newRoomNotification), stopNewMessage: () => stop(newMessageNotification), diff --git a/apps/meteor/tests/e2e/notification-sounds.spec.ts b/apps/meteor/tests/e2e/notification-sounds.spec.ts new file mode 100644 index 0000000000000..d6328c5d005ac --- /dev/null +++ b/apps/meteor/tests/e2e/notification-sounds.spec.ts @@ -0,0 +1,130 @@ +import type { Page } from 'playwright-core'; + +import { Users } from './fixtures/userStates'; +import { HomeChannel } from './page-objects'; +import { createTargetChannelAndReturnFullRoom, deleteRoom, setUserPreferences } from './utils'; +import { test, expect } from './utils/test'; + +declare global { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Window { + __audioCalls: { src?: string; played?: boolean }; + } +} + +test.use({ storageState: Users.admin.state }); + +test.describe.serial('Notification Sounds', () => { + let targetChannel: string; + let targetChannelId: string; + let poHomeChannel: HomeChannel; + let user1Page: Page; + + test.beforeAll(async ({ api }) => { + const { channel } = await createTargetChannelAndReturnFullRoom(api, { + members: [Users.admin.data.username, Users.user1.data.username], + }); + targetChannel = channel.name as string; + targetChannelId = channel._id; + }); + + test.afterAll(async ({ api }) => { + await deleteRoom(api, targetChannel); + }); + + test.beforeEach(async ({ page, browser }) => { + poHomeChannel = new HomeChannel(page); + user1Page = await browser.newPage({ storageState: Users.user1.state }); + await page.goto(`/channel/${targetChannel}`); + + await page.evaluate(() => { + Audio.prototype.play = ((fn) => + function (this: HTMLAudioElement, ...args: unknown[]) { + window.__audioCalls = { src: this.src, played: false }; + const ret = fn.call(this, ...args); + window.__audioCalls.played = true; + return ret; + })(Audio.prototype.play); + }); + }); + + test.afterEach(async () => { + await user1Page.close(); + }); + + test('should play default notification sounds', async ({ page }) => { + await user1Page.goto(`/channel/${targetChannel}`); + const user1PoHomeChannel = new HomeChannel(user1Page); + await user1PoHomeChannel.content.waitForChannel(); + + await poHomeChannel.sidenav.sidebarHomeAction.click(); + + await user1PoHomeChannel.content.sendMessage(`Hello @${Users.admin.data.username} from User 1`); + + await page.waitForTimeout(100); // wait for the sound to play + + const audioCalls = await page.evaluate(() => window.__audioCalls); + expect(audioCalls).toHaveProperty('src'); + expect(audioCalls.src).toContain('chime'); + expect(audioCalls.played).toBe(true); + }); + + test.describe('Notification sound preferences', () => { + test.beforeAll(async ({ api }) => { + await setUserPreferences(api, { + newMessageNotification: 'ringtone', + }); + }); + + test.afterAll(async ({ api }) => { + await setUserPreferences(api, { + newMessageNotification: 'chime', + }); + }); + + test('should play notification sound based on user preferences', async ({ page }) => { + await user1Page.goto(`/channel/${targetChannel}`); + const user1PoHomeChannel = new HomeChannel(user1Page); + await user1PoHomeChannel.content.waitForChannel(); + + await poHomeChannel.sidenav.sidebarHomeAction.click(); + + await user1PoHomeChannel.content.sendMessage(`Hello @${Users.admin.data.username} from User 1`); + + await page.waitForTimeout(100); // wait for the sound to play + + const audioCalls = await page.evaluate(() => window.__audioCalls); + expect(audioCalls).toHaveProperty('src'); + expect(audioCalls.src).toContain('ringtone'); + expect(audioCalls.played).toBe(true); + }); + }); + + test.describe('Custom room notification preferences', () => { + test.beforeEach(async ({ api }) => { + await api.post('/rooms.saveNotification', { + roomId: targetChannelId, + notifications: { + audioNotificationValue: 'door', + }, + }); + }); + + test('should play custom room notification sound', async ({ page }) => { + await user1Page.goto(`/channel/${targetChannel}`); + const user1PoHomeChannel = new HomeChannel(user1Page); + await user1PoHomeChannel.content.waitForChannel(); + + await poHomeChannel.sidenav.sidebarHomeAction.click(); + + await user1PoHomeChannel.content.sendMessage(`Hello @${Users.admin.data.username} from User 1`); + + await page.waitForTimeout(100); // wait for the sound to play + + const audioCalls = await page.evaluate(() => window.__audioCalls); + expect(audioCalls).toHaveProperty('src'); + expect(audioCalls.src).toContain('door'); + expect(audioCalls.played).toBe(true); + }); + }); +}); diff --git a/packages/ui-contexts/src/CustomSoundContext.ts b/packages/ui-contexts/src/CustomSoundContext.ts index 5c3838ba47c6a..a1a0d84d9e69f 100644 --- a/packages/ui-contexts/src/CustomSoundContext.ts +++ b/packages/ui-contexts/src/CustomSoundContext.ts @@ -34,6 +34,7 @@ export type CustomSoundContextValue = { playNewMessageLoop: () => void; stopNewRoom: () => void; stopNewMessage: () => void; + playNewMessageCustom: (soundId: ICustomSound['_id']) => void; }; list: ICustomSound[]; }; @@ -63,6 +64,7 @@ export const CustomSoundContext = createContext({ playNewMessageLoop: () => undefined, stopNewRoom: () => undefined, stopNewMessage: () => undefined, + playNewMessageCustom: () => undefined, }, list: [], });