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.mp3 b/public/audio/notification-new-message-1.mp3 new file mode 100644 index 0000000..3860589 Binary files /dev/null and b/public/audio/notification-new-message-1.mp3 differ 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]/(chat)/chat/[assistantId]/page.tsx b/src/app/[locale]/(chat)/chat/[assistantId]/page.tsx index 1313da7..eef4ad4 100644 --- a/src/app/[locale]/(chat)/chat/[assistantId]/page.tsx +++ b/src/app/[locale]/(chat)/chat/[assistantId]/page.tsx @@ -4,14 +4,13 @@ import { ChatModalsWrapper, MessageInputWrapper, } from "@/components/domains/chat"; -import { loadChatsList } from "@/entities/chats/loaders"; import styles from "./page.module.scss"; export default function Chat() { return (
- + diff --git a/src/app/[locale]/(chat)/chat/page.tsx b/src/app/[locale]/(chat)/chat/page.tsx index 1c19638..6199b68 100644 --- a/src/app/[locale]/(chat)/chat/page.tsx +++ b/src/app/[locale]/(chat)/chat/page.tsx @@ -10,7 +10,6 @@ import { NewMessagesWrapperSkeleton, } from "@/components/domains/chat"; import { NavigationBar } from "@/components/layout"; -import { loadChatsList } from "@/entities/chats/loaders"; import styles from "./page.module.scss"; @@ -19,23 +18,21 @@ export const revalidate = 0; export const fetchCache = "force-no-store"; export default function Chats() { - const chatsPromise = loadChatsList(); - return (
}> - + }> - + }> - +
- +
); } diff --git a/src/app/[locale]/(core)/layout.tsx b/src/app/[locale]/(core)/layout.tsx index 654e78a..a2730b8 100644 --- a/src/app/[locale]/(core)/layout.tsx +++ b/src/app/[locale]/(core)/layout.tsx @@ -1,5 +1,4 @@ import { DrawerProvider, Header, NavigationBar } from "@/components/layout"; -import { loadChatsList } from "@/entities/chats/loaders"; import { ChatStoreProvider } from "@/providers/chat-store-provider"; import styles from "./layout.module.scss"; @@ -9,13 +8,12 @@ export default function CoreLayout({ }: Readonly<{ children: React.ReactNode; }>) { - const chatsPromise = loadChatsList(); return ( -
+
{children}
- + ); diff --git a/src/app/[locale]/(core)/page.tsx b/src/app/[locale]/(core)/page.tsx index 11cb9ab..54a0512 100644 --- a/src/app/[locale]/(core)/page.tsx +++ b/src/app/[locale]/(core)/page.tsx @@ -28,7 +28,7 @@ export default function Home() { return (
}> - + diff --git a/src/app/[locale]/(payment)/layout.tsx b/src/app/[locale]/(payment)/layout.tsx index a717e7d..f8c99ba 100644 --- a/src/app/[locale]/(payment)/layout.tsx +++ b/src/app/[locale]/(payment)/layout.tsx @@ -1,5 +1,4 @@ import { DrawerProvider, Header } from "@/components/layout"; -import { loadChatsList } from "@/entities/chats/loaders"; import styles from "./layout.module.scss"; @@ -10,7 +9,7 @@ export default function CoreLayout({ }>) { return ( -
+
{children}
); diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index 10550cf..f9b67b4 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -10,10 +10,13 @@ import { getMessages } from "next-intl/server"; import clsx from "clsx"; import YandexMetrika from "@/components/analytics/YandexMetrika"; +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"; import SocketProvider from "@/providers/socket-provider"; import { ToastProvider } from "@/providers/toast-provider"; @@ -60,6 +63,7 @@ export default async function RootLayout({ const user = await loadUser(); const userId = await loadUserId(); + const chats = await loadChatsList(); return ( @@ -69,11 +73,15 @@ export default async function RootLayout({ - - - {children} - - + + + + + {children} + + + + diff --git a/src/components/domains/chat/ChatCategories/ChatCategories.tsx b/src/components/domains/chat/ChatCategories/ChatCategories.tsx index 33a3b13..8dadc63 100644 --- a/src/components/domains/chat/ChatCategories/ChatCategories.tsx +++ b/src/components/domains/chat/ChatCategories/ChatCategories.tsx @@ -1,23 +1,17 @@ "use client"; -import { use, useState } from "react"; +import { useState } from "react"; import { Skeleton } from "@/components/ui"; import { Chips } from "@/components/widgets"; -import { IGetChatsListResponse } from "@/entities/chats/types"; -import { useChatsSocket } from "@/hooks/chats/useChatsSocket"; +import { useChats } from "@/providers/chats-provider"; import { CategoryChats, ChatItemsList } from ".."; const MAX_HIDE_VISIBLE_COUNT = 3; -interface ChatCategoriesProps { - chatsPromise: Promise; -} - -export default function ChatCategories({ chatsPromise }: ChatCategoriesProps) { - const chats = use(chatsPromise); - const { categorizedChats } = useChatsSocket({ initialChats: chats }); +export default function ChatCategories() { + const { categorizedChats } = useChats(); const [activeChip, setActiveChip] = useState("All"); const [maxVisibleChats, setMaxVisibleChats] = useState< diff --git a/src/components/domains/chat/ChatHeader/ChatHeader.tsx b/src/components/domains/chat/ChatHeader/ChatHeader.tsx index 4cb8074..f54befe 100644 --- a/src/components/domains/chat/ChatHeader/ChatHeader.tsx +++ b/src/components/domains/chat/ChatHeader/ChatHeader.tsx @@ -1,6 +1,6 @@ "use client"; -import { use, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; @@ -13,26 +13,20 @@ import { UserAvatar, } from "@/components/ui"; import { revalidateChatsPage } from "@/entities/chats/actions"; -import { IGetChatsListResponse } from "@/entities/chats/types"; -import { useChatsSocket } from "@/hooks/chats/useChatsSocket"; import { useChat } from "@/providers/chat-provider"; import { useChatStore } from "@/providers/chat-store-provider"; +import { useChats } from "@/providers/chats-provider"; import { formatSecondsToHHMMSS } from "@/shared/utils/date"; import { delay } from "@/shared/utils/delay"; import styles from "./ChatHeader.module.scss"; -interface ChatHeaderProps { - chatsPromise: Promise; -} - -export default function ChatHeader({ chatsPromise }: ChatHeaderProps) { +export default function ChatHeader() { const t = useTranslations("Chat"); const router = useRouter(); const currentChat = useChatStore(state => state.currentChat); const { isLoadingAdvisorMessage, isAvailableChatting } = useChat(); - const chats = use(chatsPromise); - const { totalUnreadCount } = useChatsSocket({ initialChats: chats }); + const { totalUnreadCount } = useChats(); const [timer, setTimer] = useState(0); useEffect(() => { diff --git a/src/components/domains/chat/CorrespondenceStartedWrapper/CorrespondenceStartedWrapper.tsx b/src/components/domains/chat/CorrespondenceStartedWrapper/CorrespondenceStartedWrapper.tsx index 658bfea..b488bab 100644 --- a/src/components/domains/chat/CorrespondenceStartedWrapper/CorrespondenceStartedWrapper.tsx +++ b/src/components/domains/chat/CorrespondenceStartedWrapper/CorrespondenceStartedWrapper.tsx @@ -1,25 +1,16 @@ "use client"; -import { use } from "react"; import { useTranslations } from "next-intl"; import { Skeleton } from "@/components/ui"; -import { IGetChatsListResponse } from "@/entities/chats/types"; -import { useChatsSocket } from "@/hooks/chats/useChatsSocket"; import { useAppUiStore } from "@/providers/app-ui-store-provider"; +import { useChats } from "@/providers/chats-provider"; import { ChatItemsList, CorrespondenceStarted } from ".."; -interface CorrespondenceStartedWrapperProps { - chatsPromise: Promise; -} - -export default function CorrespondenceStartedWrapper({ - chatsPromise, -}: CorrespondenceStartedWrapperProps) { +export default function CorrespondenceStartedWrapper() { const t = useTranslations("Chat"); - const chats = use(chatsPromise); - const { startedChats } = useChatsSocket({ initialChats: chats }); + const { startedChats } = useChats(); const { isVisibleAll } = useAppUiStore( state => state.chats.correspondenceStarted diff --git a/src/components/domains/chat/NewMessagesWrapper/NewMessagesWrapper.tsx b/src/components/domains/chat/NewMessagesWrapper/NewMessagesWrapper.tsx index 18ab9b4..282a007 100644 --- a/src/components/domains/chat/NewMessagesWrapper/NewMessagesWrapper.tsx +++ b/src/components/domains/chat/NewMessagesWrapper/NewMessagesWrapper.tsx @@ -1,25 +1,16 @@ "use client"; -import { use } from "react"; import { useTranslations } from "next-intl"; import { Skeleton } from "@/components/ui"; -import { IGetChatsListResponse } from "@/entities/chats/types"; -import { useChatsSocket } from "@/hooks/chats/useChatsSocket"; import { useAppUiStore } from "@/providers/app-ui-store-provider"; +import { useChats } from "@/providers/chats-provider"; import { ChatItemsList, NewMessages } from ".."; -interface NewMessagesWrapperProps { - chatsPromise: Promise; -} - -export default function NewMessagesWrapper({ - chatsPromise, -}: NewMessagesWrapperProps) { +export default function NewMessagesWrapper() { const t = useTranslations("Chat"); - const chats = use(chatsPromise); - const { unreadChats } = useChatsSocket({ initialChats: chats }); + const { unreadChats } = useChats(); const { isVisibleAll } = useAppUiStore(state => state.chats.newMessages); const hasHydrated = useAppUiStore(state => state._hasHydrated); diff --git a/src/components/domains/dashboard/sections/NewMessagesSection/NewMessagesSection.tsx b/src/components/domains/dashboard/sections/NewMessagesSection/NewMessagesSection.tsx index c5a31ca..3b8d670 100644 --- a/src/components/domains/dashboard/sections/NewMessagesSection/NewMessagesSection.tsx +++ b/src/components/domains/dashboard/sections/NewMessagesSection/NewMessagesSection.tsx @@ -1,24 +1,14 @@ "use client"; -import { use } from "react"; - import { NewMessages, ViewAll } from "@/components/domains/chat"; import { Skeleton } from "@/components/ui"; -import { IGetChatsListResponse } from "@/entities/chats/types"; -import { useChatsSocket } from "@/hooks/chats/useChatsSocket"; import { useAppUiStore } from "@/providers/app-ui-store-provider"; +import { useChats } from "@/providers/chats-provider"; import styles from "./NewMessagesSection.module.scss"; -interface NewMessagesSectionProps { - chatsPromise: Promise; -} - -export default function NewMessagesSection({ - chatsPromise, -}: NewMessagesSectionProps) { - const chats = use(chatsPromise); - const { unreadChats } = useChatsSocket({ initialChats: chats }); +export default function NewMessagesSection() { + const { unreadChats } = useChats(); const { isVisibleAll } = useAppUiStore(state => state.home.newMessages); const hasHydrated = useAppUiStore(state => state._hasHydrated); diff --git a/src/components/layout/Header/Header.tsx b/src/components/layout/Header/Header.tsx index 1aa5253..5a10f1a 100644 --- a/src/components/layout/Header/Header.tsx +++ b/src/components/layout/Header/Header.tsx @@ -1,12 +1,10 @@ "use client"; -import { use } from "react"; import Link from "next/link"; import clsx from "clsx"; import { Badge, Button, Icon, IconName, Typography } from "@/components/ui"; -import { IGetChatsListResponse } from "@/entities/chats/types"; -import { useChatsSocket } from "@/hooks/chats/useChatsSocket"; +import { useChats } from "@/providers/chats-provider"; import { ROUTES } from "@/shared/constants/client-routes"; import { useDrawer } from ".."; @@ -16,13 +14,11 @@ import styles from "./Header.module.scss"; interface HeaderProps { className?: string; - chatsPromise: Promise; } -export default function Header({ className, chatsPromise }: HeaderProps) { +export default function Header({ className }: HeaderProps) { const { open } = useDrawer(); - const chats = use(chatsPromise); - const { totalUnreadCount } = useChatsSocket({ initialChats: chats }); + const { totalUnreadCount } = useChats(); return (
diff --git a/src/components/layout/NavigationBar/NavigationBar.tsx b/src/components/layout/NavigationBar/NavigationBar.tsx index eb3639d..45a0aee 100644 --- a/src/components/layout/NavigationBar/NavigationBar.tsx +++ b/src/components/layout/NavigationBar/NavigationBar.tsx @@ -1,14 +1,12 @@ "use client"; -import { use } from "react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { useLocale } from "next-intl"; import clsx from "clsx"; import { Badge, Icon, Typography } from "@/components/ui"; -import { IGetChatsListResponse } from "@/entities/chats/types"; -import { useChatsSocket } from "@/hooks/chats/useChatsSocket"; +import { useChats } from "@/providers/chats-provider"; import { ROUTES } from "@/shared/constants/client-routes"; import { NavItem, navItems } from "@/shared/constants/navigation"; import { stripLocale } from "@/shared/utils/path"; @@ -20,16 +18,11 @@ const getBadge = (item: NavItem, totalUnreadCount: number) => { return null; }; -interface NavigationBarProps { - chatsPromise: Promise; -} - -export default function NavigationBar({ chatsPromise }: NavigationBarProps) { +export default function NavigationBar() { const pathname = usePathname(); const locale = useLocale(); const pathnameWithoutLocale = stripLocale(pathname, locale); - const chats = use(chatsPromise); - const { totalUnreadCount } = useChatsSocket({ initialChats: chats }); + const { totalUnreadCount } = useChats(); return (