diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 99ceed02..9d1e01d9 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -24,6 +24,7 @@ "react-aria-components": "1.2.1", "react-dom": "18.2.0", "react-i18next": "14.1.3", + "use-sound": "^4.0.3", "valtio": "1.13.2", "wouter": "3.3.0" }, @@ -6695,6 +6696,11 @@ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", "dev": true }, + "node_modules/howler": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.4.tgz", + "integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w==" + }, "node_modules/html-parse-stringify": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", @@ -10162,6 +10168,17 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sound": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/use-sound/-/use-sound-4.0.3.tgz", + "integrity": "sha512-L205pEUFIrLsGYsCUKHQVCt0ajs//YQOFbEQeNwaWaqQj3y3st4SuR+rvpMHLmv8hgTcfUFlvMQawZNI3OE18w==", + "dependencies": { + "howler": "^2.1.3" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, "node_modules/use-sync-external-store": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", diff --git a/src/frontend/package.json b/src/frontend/package.json index 406bf0d2..03b34d3d 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -29,6 +29,7 @@ "react-aria-components": "1.2.1", "react-dom": "18.2.0", "react-i18next": "14.1.3", + "use-sound": "4.0.3", "valtio": "1.13.2", "wouter": "3.3.0" }, diff --git a/src/frontend/public/sounds/notifications.mp3 b/src/frontend/public/sounds/notifications.mp3 new file mode 100644 index 00000000..83a7cd51 Binary files /dev/null and b/src/frontend/public/sounds/notifications.mp3 differ diff --git a/src/frontend/src/features/notifications/MainNotificationToast.tsx b/src/frontend/src/features/notifications/MainNotificationToast.tsx index e04ab46d..50739f78 100644 --- a/src/frontend/src/features/notifications/MainNotificationToast.tsx +++ b/src/frontend/src/features/notifications/MainNotificationToast.tsx @@ -4,15 +4,19 @@ import { Participant, RemoteParticipant, RoomEvent } from 'livekit-client' import { ToastProvider, toastQueue } from './components/ToastProvider' import { NotificationType } from './NotificationType' import { Div } from '@/primitives' -import { isMobileBrowser } from '@livekit/components-core/src/helper/detectMobileBrowser.ts' +import { isMobileBrowser } from '@livekit/components-core' +import { useNotificationSound } from '@/features/notifications/hooks/useSoundNotification' export const MainNotificationToast = () => { const room = useRoomContext() + const { triggerNotificationSound } = useNotificationSound() + useEffect(() => { const showJoinNotification = (participant: Participant) => { if (isMobileBrowser()) { return } + triggerNotificationSound(NotificationType.Joined) toastQueue.add( { participant, @@ -27,7 +31,7 @@ export const MainNotificationToast = () => { return () => { room.off(RoomEvent.ParticipantConnected, showJoinNotification) } - }, [room]) + }, [room, triggerNotificationSound]) useEffect(() => { const removeJoinNotification = (participant: Participant) => { @@ -71,6 +75,7 @@ export const MainNotificationToast = () => { return } if (!existingToast && notification === NotificationType.Raised) { + triggerNotificationSound(NotificationType.Raised) toastQueue.add( { participant, @@ -86,7 +91,7 @@ export const MainNotificationToast = () => { return () => { room.off(RoomEvent.DataReceived, handleNotificationReceived) } - }, [room]) + }, [room, triggerNotificationSound]) return (
diff --git a/src/frontend/src/features/notifications/hooks/useSoundNotification.tsx b/src/frontend/src/features/notifications/hooks/useSoundNotification.tsx new file mode 100644 index 00000000..145f8110 --- /dev/null +++ b/src/frontend/src/features/notifications/hooks/useSoundNotification.tsx @@ -0,0 +1,18 @@ +import useSound from 'use-sound' + +// fixme - handle dynamic audio output changes +export const useNotificationSound = () => { + const [play] = useSound('./sounds/notifications.mp3', { + sprite: { + joined: [0, 1150], + raised: [1400, 180], + message: [1580, 300], + waiting: [2039, 710], + success: [2740, 1304], + }, + }) + const triggerNotificationSound = (type: string) => { + play({ id: type }) + } + return { triggerNotificationSound } +}