From fbd367fb82d83e48b3f0f72e641129737193b79f Mon Sep 17 00:00:00 2001 From: bahaa Date: Wed, 22 Nov 2023 09:40:01 +0300 Subject: [PATCH 1/3] Use howler unlock feature to play audio --- package.json | 2 ++ src/hooks/useGetActiveChats.ts | 10 +++---- src/hooks/useGetNewMessages.ts | 12 ++++++--- src/hooks/useNewMessageNotification.ts | 11 ++++---- src/hooks/useSound.ts | 29 +++++++++++++++++++++ src/services/sse.service.ts | 36 ++++++++++---------------- 6 files changed, 62 insertions(+), 38 deletions(-) create mode 100644 src/hooks/useSound.ts diff --git a/package.json b/package.json index 93c8290..41ed765 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "classnames": "^2.3.1", "cross-env": "^7.0.3", "framer-motion": "4.1.17", + "howler": "^2.2.4", "i18next": "^21.2.4", "i18next-browser-languagedetector": "^6.1.2", "linkify-react": "^3.0.3", @@ -40,6 +41,7 @@ "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^10.4.9", "@testing-library/user-event": "^7.2.1", + "@types/howler": "^2.2.11", "@types/luxon": "^2.0.8", "eslint": "8.15.0", "eslint-config-airbnb": "^18.2.1", diff --git a/src/hooks/useGetActiveChats.ts b/src/hooks/useGetActiveChats.ts index 19721b8..fa4a40a 100644 --- a/src/hooks/useGetActiveChats.ts +++ b/src/hooks/useGetActiveChats.ts @@ -9,16 +9,16 @@ const useGetActiveChats = (): void => { const dispatch = useAppDispatch(); useEffect(() => { - const sseInstance = sse('cs-get-all-active-chats'); - - sseInstance.onMessage((data: Chat[]) => { + const onMessage = (data: Chat[]) => { if (data !== undefined) { dispatch(setActiveChats({ customerSupportId, data, checkNewMessages: true })); } - }); + }; + + const events = sse('cs-get-all-active-chats', onMessage); return () => { - sseInstance.close(); + events.close(); }; }, [customerSupportId, dispatch]); }; diff --git a/src/hooks/useGetNewMessages.ts b/src/hooks/useGetNewMessages.ts index ea09ff0..c5b72b2 100644 --- a/src/hooks/useGetNewMessages.ts +++ b/src/hooks/useGetNewMessages.ts @@ -12,14 +12,18 @@ const useGetNewMessages = (chatId: string | undefined): void => { useEffect(() => { if (!chatId || chatId === '-1' || !isAuthenticated || !selectedActiveChat) return undefined; - const sseInstance = sse(`cs-get-new-messages?chatId=${chatId}&lastRead=${lastReadMessageDate.split('+')[0]}`); - sseInstance.onMessage((data: MessageModel[]) => { + const onMessage = (data: MessageModel[]) => { dispatch(addNewMessages(data)); - }); + }; + + const events = sse( + `cs-get-new-messages?chatId=${chatId}&lastRead=${lastReadMessageDate.split('+')[0]}`, + onMessage + ); return () => { - sseInstance.close(); + events.close(); }; }, [isAuthenticated, dispatch, lastReadMessageDate, chatId, selectedActiveChat]); }; diff --git a/src/hooks/useNewMessageNotification.ts b/src/hooks/useNewMessageNotification.ts index 6bdcf10..077c17f 100644 --- a/src/hooks/useNewMessageNotification.ts +++ b/src/hooks/useNewMessageNotification.ts @@ -1,23 +1,22 @@ import { useEffect } from 'react'; import { useSelector } from 'react-redux'; -import useSound from 'use-sound'; import { RootState, useAppDispatch } from '../store'; import { resetNewMessagesAmount } from '../slices/chats.slice'; -import dingMp3 from '../static/ding.mp3'; +import { useDing } from './useSound'; const useNewMessageNotification = (): void => { const newMessagesAmount = useSelector((state: RootState) => state.chats.newMessagesAmount); - const [ding] = useSound(dingMp3); + const [ding] = useDing(); const dispatch = useAppDispatch(); const title = 'Bürokratt'; - + useEffect(() => { const onVisibilityChange = () => { document.title = title; dispatch(resetNewMessagesAmount()); }; - document.addEventListener('visibilitychange', onVisibilityChange); + document.addEventListener('visibilitychange', onVisibilityChange, false); return () => { document.removeEventListener('visibilitychange', onVisibilityChange); @@ -26,8 +25,8 @@ const useNewMessageNotification = (): void => { useEffect(() => { if (newMessagesAmount === 0) return; + ding?.play(); document.title = `(${newMessagesAmount}) uus sõnum! - ${title}`; - ding(); }, [newMessagesAmount]); }; diff --git a/src/hooks/useSound.ts b/src/hooks/useSound.ts new file mode 100644 index 0000000..29ad123 --- /dev/null +++ b/src/hooks/useSound.ts @@ -0,0 +1,29 @@ +import { useEffect, useState } from 'react'; +import { Howl } from 'howler'; + +export const useAudio = (audiosrc: string) => { + const [audio, setAudio] = useState(null); + + useEffect(() => { + const howl = new Howl({ + src: audiosrc, + onloaderror: (soundId, error) => console.error(soundId, error), + onplayerror: (soundId, error) => { + console.error(soundId, error); + howl.once('unlock', () => howl.play()); + }, + }); + + setAudio(howl); + + return () => { + howl.unload(); + } + }, []); + + return [audio] as const; +} + +export const useDing = () => { + return useAudio(`${process.env.PUBLIC_URL}/ding.mp3`); +} diff --git a/src/services/sse.service.ts b/src/services/sse.service.ts index fcdf934..46b10b7 100644 --- a/src/services/sse.service.ts +++ b/src/services/sse.service.ts @@ -2,34 +2,24 @@ import { RuuterResponse } from '../model/ruuter-response.model'; const ruuterUrl = window._env_.RUUTER_API_URL; -interface SseInstance { - onMessage: (handleData: (data: T) => void) => void; - close: () => void; -} - -const sse = (url: string): SseInstance => { +const sse = (url: string, onMessage: (data: T) => void) => { const eventSource = new EventSource(`${ruuterUrl}/sse/${url}`, { withCredentials: true }); - const onMessage = (handleData: (data: T) => void) => { - eventSource.onmessage = (event: MessageEvent) => { - const response = JSON.parse(event.data); + eventSource.onmessage = (event: MessageEvent) => { + const response = JSON.parse(event.data); - if (response.statusCodeValue === 200) { - const ruuterResponse = response.body as RuuterResponse; - if (ruuterResponse.data) handleData(Object.values(ruuterResponse.data)[0] as T); - } - }; - }; - - const close = () => { - eventSource.close(); - }; + if (response.statusCodeValue === 200) { + const ruuterResponse = response.body as RuuterResponse; + if (ruuterResponse?.data) + onMessage(Object.values(ruuterResponse.data)[0] as T); + } + } eventSource.onerror = () => { - eventSource.close(); - }; + console.error('SSE error'); + } - return { onMessage, close }; -}; + return eventSource; +} export default sse; From 505953401fbb74299bf222c6adf580154edfd4d8 Mon Sep 17 00:00:00 2001 From: bahaa Date: Wed, 22 Nov 2023 10:21:27 +0300 Subject: [PATCH 2/3] Import sound effect file from static folder --- src/hooks/useSound.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hooks/useSound.ts b/src/hooks/useSound.ts index 29ad123..6c81e0a 100644 --- a/src/hooks/useSound.ts +++ b/src/hooks/useSound.ts @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react'; import { Howl } from 'howler'; +import ding from '../static/ding.mp3'; export const useAudio = (audiosrc: string) => { const [audio, setAudio] = useState(null); @@ -25,5 +26,5 @@ export const useAudio = (audiosrc: string) => { } export const useDing = () => { - return useAudio(`${process.env.PUBLIC_URL}/ding.mp3`); + return useAudio(ding); } From 4c1fcf79851d0e493e4ba13ccdcad36dda352889 Mon Sep 17 00:00:00 2001 From: bahaa Date: Wed, 22 Nov 2023 14:52:23 +0300 Subject: [PATCH 3/3] Update sound effect file --- src/static/ding.mp3 | Bin 65574 -> 26266 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/static/ding.mp3 b/src/static/ding.mp3 index 917c9b885d6f22e1d3ebf77a81f36a6365e9314f..af75c65a52ac4e99c8d3e26b688552fc6169259a 100644 GIT binary patch delta 193 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