diff --git a/global.d.ts b/global.d.ts
new file mode 100644
index 0000000..98af3d3
--- /dev/null
+++ b/global.d.ts
@@ -0,0 +1,7 @@
+declare global {
+ interface Window {
+ webkitAudioContext: typeof AudioContext;
+ }
+}
+
+export {};
diff --git a/public/audio/notification-new-message-1.wav b/public/audio/notification-new-message-1.wav
new file mode 100644
index 0000000..c7017ad
Binary files /dev/null and b/public/audio/notification-new-message-1.wav differ
diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
index 361e368..f9b67b4 100644
--- a/src/app/[locale]/layout.tsx
+++ b/src/app/[locale]/layout.tsx
@@ -14,6 +14,7 @@ import { loadChatsList } from "@/entities/chats/loaders";
import { loadUser, loadUserId } from "@/entities/user/loaders";
import { routing } from "@/i18n/routing";
import { AppUiStoreProvider } from "@/providers/app-ui-store-provider";
+import { AudioProvider } from "@/providers/audio-provider";
import { ChatsInitializationProvider } from "@/providers/chats-initialization-provider";
import { ChatsProvider } from "@/providers/chats-provider";
import { RetainingStoreProvider } from "@/providers/retaining-store-provider";
@@ -72,13 +73,15 @@ export default async function RootLayout({
-
-
-
- {children}
-
-
-
+
+
+
+
+ {children}
+
+
+
+
diff --git a/src/hooks/audio/useAudio.ts b/src/hooks/audio/useAudio.ts
index d6b7bce..03143d1 100644
--- a/src/hooks/audio/useAudio.ts
+++ b/src/hooks/audio/useAudio.ts
@@ -2,35 +2,48 @@
import { useCallback, useEffect, useMemo } from "react";
-import { audioService } from "@/services/audio";
-import { AudioUrls } from "@/shared/constants/audio";
-
-interface UseAudioOptions {
- enabled?: boolean;
- preload?: AudioUrls[];
-}
-
-export const useAudio = (options: UseAudioOptions = {}) => {
- const { enabled = true, preload = [] } = options;
-
- useEffect(() => {
- audioService.setEnabled(enabled);
- }, [enabled]);
-
- useEffect(() => {
- if (preload.length > 0) {
- audioService.preload(preload);
+export const useAudio = () => {
+ const audio = useMemo(() => {
+ if (typeof window !== "undefined") {
+ const audioElement = new Audio("/audio/notification-new-message-1.wav");
+ audioElement.preload = "auto";
+ return audioElement;
}
- }, [preload]);
+ return null;
+ }, []);
+
+ const _audioContext = useMemo(() => {
+ if (typeof window !== "undefined") {
+ return new (window.AudioContext || window.webkitAudioContext)();
+ }
+ return null;
+ }, []);
+
+ useEffect(() => {
+ if (!audio) return;
+
+ const handleClick = () => {
+ audio.currentTime = 0;
+ audio.play();
+ setTimeout(() => {
+ audio.pause();
+ }, 10);
+ };
+ document.addEventListener("click", handleClick, { once: true });
+ return () => {
+ document.removeEventListener("click", handleClick);
+ };
+ }, [audio]);
const playNewMessageNotification = useCallback(() => {
- audioService.play(AudioUrls.NewMessage);
- }, []);
+ if (!audio) return;
+ audio.currentTime = 0;
+ audio.play();
+ }, [audio]);
return useMemo(
() => ({
playNewMessageNotification,
- isEnabled: audioService.isAudioEnabled(),
}),
[playNewMessageNotification]
);
diff --git a/src/hooks/chats/useChatsSocket.ts b/src/hooks/chats/useChatsSocket.ts
index 08ed718..fac3659 100644
--- a/src/hooks/chats/useChatsSocket.ts
+++ b/src/hooks/chats/useChatsSocket.ts
@@ -3,9 +3,8 @@
import { useMemo, useState } from "react";
import { IGetChatsListResponse } from "@/entities/chats/types";
-import { AudioUrls } from "@/shared/constants/audio";
+import { useAudioContext } from "@/providers/audio-provider";
-import { useAudio } from "../audio/useAudio";
import { useSocketEvent } from "../socket/useSocketEvent";
export interface UseChatsSocketOptions {
@@ -14,8 +13,6 @@ export interface UseChatsSocketOptions {
}
export const useChatsSocket = (options: UseChatsSocketOptions = {}) => {
- const { enableNotificationSound = true } = options;
-
const initialChats = options.initialChats ?? {
categorizedChats: {},
startedChats: [],
@@ -23,10 +20,7 @@ export const useChatsSocket = (options: UseChatsSocketOptions = {}) => {
totalUnreadCount: 0,
};
- const { playNewMessageNotification } = useAudio({
- enabled: enableNotificationSound,
- preload: [AudioUrls.NewMessage],
- });
+ const { playNewMessageNotification } = useAudioContext();
const [isInChat, setIsInChat] = useState(false);
const [chats, setChats] = useState(initialChats);
diff --git a/src/providers/audio-provider.tsx b/src/providers/audio-provider.tsx
new file mode 100644
index 0000000..afeb44f
--- /dev/null
+++ b/src/providers/audio-provider.tsx
@@ -0,0 +1,29 @@
+"use client";
+
+import { createContext, ReactNode, useContext } from "react";
+
+import { useAudio } from "@/hooks/audio/useAudio";
+
+type AudioContextValue = ReturnType;
+
+const AudioContext = createContext(null);
+
+export function useAudioContext() {
+ const ctx = useContext(AudioContext);
+ if (!ctx) {
+ throw new Error("useAudio must be used within ");
+ }
+ return ctx;
+}
+
+interface AudioProviderProps {
+ children: ReactNode;
+}
+
+export function AudioProvider({ children }: AudioProviderProps) {
+ const value = useAudio();
+
+ return (
+ {children}
+ );
+}
diff --git a/src/services/audio/index.ts b/src/services/audio/index.ts
deleted file mode 100644
index 515f9d5..0000000
--- a/src/services/audio/index.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-"use client";
-
-import { AudioUrls, getAudioUrl } from "@/shared/constants/audio";
-
-class AudioService {
- private audioElements = new Map();
- private isEnabled = true;
-
- private getAudioElement(url: string): HTMLAudioElement | undefined {
- if (!this.audioElements.has(url)) {
- const audio = new Audio(url);
- audio.preload = "auto";
- this.audioElements.set(url, audio);
- }
- return this.audioElements.get(url);
- }
-
- play(key: AudioUrls): Promise {
- if (!this.isEnabled) return Promise.resolve();
-
- try {
- const audio = this.getAudioElement(getAudioUrl(key));
- if (audio) {
- audio.currentTime = 0;
- return audio.play();
- }
- return Promise.resolve();
- } catch (error) {
- // eslint-disable-next-line no-console
- console.warn("Failed to play audio:", error);
- return Promise.resolve();
- }
- }
-
- setEnabled(enabled: boolean): void {
- this.isEnabled = enabled;
- }
-
- isAudioEnabled(): boolean {
- return this.isEnabled;
- }
-
- preload(keys: AudioUrls[]): void {
- keys.forEach(key => {
- this.getAudioElement(getAudioUrl(key));
- });
- }
-
- dispose(): void {
- this.audioElements.forEach(audio => {
- audio.pause();
- audio.src = "";
- });
- this.audioElements.clear();
- }
-}
-
-export const audioService = new AudioService();
diff --git a/src/shared/constants/audio/index.ts b/src/shared/constants/audio/index.ts
deleted file mode 100644
index 308581f..0000000
--- a/src/shared/constants/audio/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-const audioUrls: Record = {
- "new-message": "/audio/notification-new-message-1.mp3",
-};
-
-export enum AudioUrls {
- NewMessage = "new-message",
-}
-
-export const getAudioUrl = (key: AudioUrls) => audioUrls[key];