/** * GlobalSidebarContext - 全局右侧工具栏状态管理 * * 管理侧边栏的展开/收起状态和数据加载 * 自选股和关注事件数据都从 Redux 获取,与导航栏共用数据源 */ import React, { createContext, useContext, useState, useCallback, useEffect, useRef } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { useAuth } from './AuthContext'; import { logger } from '@/utils/logger'; import { loadWatchlist, loadWatchlistQuotes, toggleWatchlist, loadFollowingEvents, loadEventComments, toggleFollowEvent } from '@/store/slices/stockSlice'; const GlobalSidebarContext = createContext(null); /** * GlobalSidebarProvider - 全局侧边栏 Provider */ export const GlobalSidebarProvider = ({ children }) => { const { user } = useAuth(); const userId = user?.id; const dispatch = useDispatch(); // 侧边栏展开/收起状态(默认折叠) const [isOpen, setIsOpen] = useState(false); // 从 Redux 获取自选股数据(与导航栏共用) const watchlist = useSelector(state => state.stock.watchlist || []); const watchlistQuotes = useSelector(state => state.stock.watchlistQuotes || []); const watchlistLoading = useSelector(state => state.stock.loading?.watchlist); const quotesLoading = useSelector(state => state.stock.loading?.watchlistQuotes); // 将 watchlistQuotes 数组转换为 { stock_code: quote } 格式(兼容现有组件) const realtimeQuotes = React.useMemo(() => { const quotesMap = {}; watchlistQuotes.forEach(item => { quotesMap[item.stock_code] = item; }); return quotesMap; }, [watchlistQuotes]); // 从 Redux 获取关注事件数据(与导航栏共用) const followingEvents = useSelector(state => state.stock.followingEvents || []); const eventComments = useSelector(state => state.stock.eventComments || []); const eventsLoading = useSelector(state => state.stock.loading?.followingEvents || false); // 防止重复加载 const hasLoadedRef = useRef(false); /** * 切换侧边栏展开/收起 */ const toggle = useCallback(() => { setIsOpen(prev => !prev); }, []); /** * 加载实时行情(通过 Redux) */ const loadRealtimeQuotes = useCallback(() => { if (!userId) return; dispatch(loadWatchlistQuotes()); }, [userId, dispatch]); /** * 加载所有数据(自选股和关注事件都从 Redux 获取) */ const loadData = useCallback(() => { if (!userId) return; // 自选股通过 Redux 加载 dispatch(loadWatchlist()); dispatch(loadWatchlistQuotes()); // 关注事件和评论通过 Redux 加载 dispatch(loadFollowingEvents()); dispatch(loadEventComments()); }, [userId, dispatch]); /** * 刷新数据 */ const refresh = useCallback(async () => { await loadData(); }, [loadData]); /** * 取消关注股票(通过 Redux) */ const unwatchStock = useCallback(async (stockCode) => { if (!userId) return; try { // 找到股票名称 const stockItem = watchlist.find(s => s.stock_code === stockCode); const stockName = stockItem?.stock_name || ''; // 通过 Redux action 移除(乐观更新) await dispatch(toggleWatchlist({ stockCode, stockName, isInWatchlist: true // 表示当前在自选股中,需要移除 })).unwrap(); logger.debug('GlobalSidebar', 'unwatchStock 成功', { stockCode }); } catch (error) { logger.error('GlobalSidebar', 'unwatchStock', error, { stockCode, userId }); } }, [userId, dispatch, watchlist]); /** * 取消关注事件(通过 Redux) */ const unfollowEvent = useCallback(async (eventId) => { if (!userId) return; try { // 通过 Redux action 取消关注(乐观更新) await dispatch(toggleFollowEvent({ eventId, isFollowing: true // 表示当前已关注,需要取消 })).unwrap(); logger.debug('GlobalSidebar', 'unfollowEvent 成功', { eventId }); } catch (error) { logger.error('GlobalSidebar', 'unfollowEvent', error, { eventId, userId }); // 失败时重新加载列表 dispatch(loadFollowingEvents()); } }, [userId, dispatch]); // 用户登录后加载数据 useEffect(() => { if (user && !hasLoadedRef.current) { console.log('[GlobalSidebar] 用户登录,加载数据'); hasLoadedRef.current = true; loadData(); } // 用户登出时重置(所有状态由 Redux 管理) if (!user) { hasLoadedRef.current = false; } }, [user, loadData]); // 页面可见性变化时刷新数据 useEffect(() => { const onVisibilityChange = () => { if (document.visibilityState === 'visible' && user) { console.log('[GlobalSidebar] 页面可见,刷新数据'); loadData(); } }; document.addEventListener('visibilitychange', onVisibilityChange); return () => document.removeEventListener('visibilitychange', onVisibilityChange); }, [user, loadData]); // 定时刷新实时行情(每分钟一次,两个面板共用) useEffect(() => { if (watchlist.length > 0 && userId) { const interval = setInterval(() => { console.log('[GlobalSidebar] 定时刷新行情'); dispatch(loadWatchlistQuotes()); }, 60000); return () => clearInterval(interval); } }, [watchlist.length, userId, dispatch]); const value = { // 状态 isOpen, toggle, // 数据(watchlist 和 realtimeQuotes 从 Redux 获取) watchlist, realtimeQuotes, followingEvents, eventComments, // 加载状态 loading: watchlistLoading || eventsLoading, quotesLoading, // 方法 refresh, loadRealtimeQuotes, unwatchStock, unfollowEvent, }; return ( {children} ); }; /** * useGlobalSidebar - 获取全局侧边栏 Context */ export const useGlobalSidebar = () => { const context = useContext(GlobalSidebarContext); if (!context) { throw new Error('useGlobalSidebar must be used within a GlobalSidebarProvider'); } return context; }; export default GlobalSidebarContext;