// src/context/UnreadContext.jsx import { createContext, useContext, useState, useCallback, useMemo } from 'react'; const UnreadContext = createContext(null); export function UnreadProvider({ children }) { const [unreadMap, setUnreadMap] = useState({}); const setChatUnread = useCallback((chatId, count) => { setUnreadMap(prev => { if (count > 0) { return { ...prev, [chatId]: count }; } if (prev[chatId] === undefined) return prev; const next = { ...prev }; delete next[chatId]; return next; }); }, []); const clearChatUnread = useCallback((chatId) => { setUnreadMap(prev => { if (prev[chatId] === undefined) return prev; const next = { ...prev }; delete next[chatId]; return next; }); }, []); /** * Инициализирует/синхронизирует unreadMap из полного списка чатов * (например, при первой загрузке через REST). Ожидает ПОЛНЫЙ список — * чаты, отсутствующие в переданном массиве, не трогает. */ const updateFromChats = useCallback((chats) => { setUnreadMap(prev => { const next = { ...prev }; for (const chat of chats) { const count = chat.unreadCount ?? chat.unreadMessages ?? 0; if (count > 0) { next[chat.id] = count; } else { delete next[chat.id]; } } return next; }); }, []); /** * Полностью сбрасывает unreadMap — вызывается при смене пользователя * (логин/логаут), чтобы счётчики предыдущей сессии не "протекали" * в новую. UnreadProvider не вложен внутрь AuthProvider, поэтому * реагировать на logout() напрямую не может — это делает компонент * ниже по дереву, имеющий доступ к обоим контекстам (см. UnreadListener в App.jsx). */ const resetUnread = useCallback(() => { setUnreadMap({}); }, []); const totalUnread = useMemo(() => Object.values(unreadMap).reduce((sum, c) => sum + c, 0), [unreadMap] ); return ( {children} ); } export function useUnread() { const ctx = useContext(UnreadContext); if (!ctx) throw new Error('useUnread must be used within UnreadProvider'); return ctx; }