src/contexts/NotificationContext.js
- 添加 selectIsMobile 导入 在 NotificationProvider 组件开头添加移动端检测 移动端返回空壳 Provider - 桌面端保持原有完整功能 移除 ConnectionStatusBar 组件和 ConnectionStatusBarWrapper(所有端) - 移除了不再使用的 useNotification、useLocation、logger 导入 - 添加了 Redux selectIsMobile 检测 - 移动端不渲染 NotificationContainer
This commit is contained in:
@@ -2,95 +2,48 @@
|
|||||||
// 集中管理应用的全局组件
|
// 集中管理应用的全局组件
|
||||||
|
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useSelector } from 'react-redux';
|
||||||
import { useNotification } from '../contexts/NotificationContext';
|
import { selectIsMobile } from '@/store/slices/deviceSlice';
|
||||||
import { logger } from '../utils/logger';
|
|
||||||
|
|
||||||
// Global Components
|
// Global Components
|
||||||
import AuthModalManager from './Auth/AuthModalManager';
|
import AuthModalManager from './Auth/AuthModalManager';
|
||||||
import NotificationContainer from './NotificationContainer';
|
import NotificationContainer from './NotificationContainer';
|
||||||
import ConnectionStatusBar from './ConnectionStatusBar';
|
|
||||||
import ScrollToTop from './ScrollToTop';
|
import ScrollToTop from './ScrollToTop';
|
||||||
|
|
||||||
// Bytedesk客服组件
|
// Bytedesk客服组件
|
||||||
import BytedeskWidget from '../bytedesk-integration/components/BytedeskWidget';
|
import BytedeskWidget from '../bytedesk-integration/components/BytedeskWidget';
|
||||||
import { getBytedeskConfig } from '../bytedesk-integration/config/bytedesk.config';
|
import { getBytedeskConfig } from '../bytedesk-integration/config/bytedesk.config';
|
||||||
|
|
||||||
/**
|
|
||||||
* ConnectionStatusBar 包装组件
|
|
||||||
* 需要在 NotificationProvider 内部使用,所以在这里包装
|
|
||||||
*/
|
|
||||||
function ConnectionStatusBarWrapper() {
|
|
||||||
const { connectionStatus, reconnectAttempt, maxReconnectAttempts, retryConnection } = useNotification();
|
|
||||||
const [isDismissed, setIsDismissed] = React.useState(false);
|
|
||||||
|
|
||||||
// 监听连接状态变化
|
|
||||||
React.useEffect(() => {
|
|
||||||
// 重连成功后,清除 dismissed 状态
|
|
||||||
if (connectionStatus === 'connected' && isDismissed) {
|
|
||||||
setIsDismissed(false);
|
|
||||||
// 从 localStorage 清除 dismissed 标记
|
|
||||||
localStorage.removeItem('connection_status_dismissed');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从 localStorage 恢复 dismissed 状态
|
|
||||||
if (connectionStatus !== 'connected' && !isDismissed) {
|
|
||||||
const dismissed = localStorage.getItem('connection_status_dismissed');
|
|
||||||
if (dismissed === 'true') {
|
|
||||||
setIsDismissed(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [connectionStatus, isDismissed]);
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
// 用户手动关闭,保存到 localStorage
|
|
||||||
setIsDismissed(true);
|
|
||||||
localStorage.setItem('connection_status_dismissed', 'true');
|
|
||||||
logger.info('App', 'Connection status bar dismissed by user');
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ConnectionStatusBar
|
|
||||||
status={connectionStatus}
|
|
||||||
reconnectAttempt={reconnectAttempt}
|
|
||||||
maxReconnectAttempts={maxReconnectAttempts}
|
|
||||||
onRetry={retryConnection}
|
|
||||||
onClose={handleClose}
|
|
||||||
isDismissed={isDismissed}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GlobalComponents - 全局组件容器
|
* GlobalComponents - 全局组件容器
|
||||||
* 集中管理所有全局级别的组件,如弹窗、通知、状态栏等
|
* 集中管理所有全局级别的组件,如弹窗、通知、状态栏等
|
||||||
*
|
*
|
||||||
* 包含的组件:
|
* 包含的组件:
|
||||||
* - ConnectionStatusBarWrapper: Socket 连接状态条
|
|
||||||
* - ScrollToTop: 路由切换时自动滚动到顶部
|
* - ScrollToTop: 路由切换时自动滚动到顶部
|
||||||
* - AuthModalManager: 认证弹窗管理器
|
* - AuthModalManager: 认证弹窗管理器
|
||||||
* - NotificationContainer: 通知容器
|
* - NotificationContainer: 通知容器(仅桌面端渲染)
|
||||||
* - BytedeskWidget: Bytedesk在线客服 (条件性显示,在/和/home页隐藏)
|
* - BytedeskWidget: Bytedesk在线客服
|
||||||
|
*
|
||||||
|
* 注意:
|
||||||
|
* - ConnectionStatusBar 已移除(所有端)
|
||||||
|
* - NotificationContainer 在移动端不渲染(通知功能已在 NotificationContext 层禁用)
|
||||||
*/
|
*/
|
||||||
export function GlobalComponents() {
|
export function GlobalComponents() {
|
||||||
const location = useLocation();
|
const isMobile = useSelector(selectIsMobile);
|
||||||
|
|
||||||
// ✅ 缓存 Bytedesk 配置对象,避免每次渲染都创建新引用导致重新加载
|
// ✅ 缓存 Bytedesk 配置对象,避免每次渲染都创建新引用导致重新加载
|
||||||
const bytedeskConfigMemo = useMemo(() => getBytedeskConfig(), []);
|
const bytedeskConfigMemo = useMemo(() => getBytedeskConfig(), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Socket 连接状态条 */}
|
|
||||||
<ConnectionStatusBarWrapper />
|
|
||||||
|
|
||||||
{/* 路由切换时自动滚动到顶部 */}
|
{/* 路由切换时自动滚动到顶部 */}
|
||||||
<ScrollToTop />
|
<ScrollToTop />
|
||||||
|
|
||||||
{/* 认证弹窗管理器 */}
|
{/* 认证弹窗管理器 */}
|
||||||
<AuthModalManager />
|
<AuthModalManager />
|
||||||
|
|
||||||
{/* 通知容器 */}
|
{/* 通知容器(仅桌面端渲染) */}
|
||||||
<NotificationContainer />
|
{!isMobile && <NotificationContainer />}
|
||||||
|
|
||||||
{/* Bytedesk在线客服 - 使用缓存的配置对象 */}
|
{/* Bytedesk在线客服 - 使用缓存的配置对象 */}
|
||||||
<BytedeskWidget
|
<BytedeskWidget
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
import React, { createContext, useContext, useState, useEffect, useCallback, useRef } from 'react';
|
import React, { createContext, useContext, useState, useEffect, useCallback, useRef } from 'react';
|
||||||
import { useToast, Box, HStack, Text, Button, CloseButton, VStack, Icon } from '@chakra-ui/react';
|
import { useToast, Box, HStack, Text, Button, CloseButton, VStack, Icon } from '@chakra-ui/react';
|
||||||
import { BellIcon } from '@chakra-ui/icons';
|
import { BellIcon } from '@chakra-ui/icons';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { selectIsMobile } from '@/store/slices/deviceSlice';
|
||||||
import { logger } from '../utils/logger';
|
import { logger } from '../utils/logger';
|
||||||
import socket from '../services/socket';
|
import socket from '../services/socket';
|
||||||
import notificationSound from '../assets/sounds/notification.wav';
|
import notificationSound from '../assets/sounds/notification.wav';
|
||||||
@@ -44,6 +46,42 @@ export const useNotification = () => {
|
|||||||
|
|
||||||
// 通知提供者组件
|
// 通知提供者组件
|
||||||
export const NotificationProvider = ({ children }) => {
|
export const NotificationProvider = ({ children }) => {
|
||||||
|
// ⚡ 移动端检测(使用 Redux 状态)
|
||||||
|
const isMobile = useSelector(selectIsMobile);
|
||||||
|
|
||||||
|
// ⚡ 移动端禁用完整通知能力:返回空壳 Provider
|
||||||
|
// 移动端不支持桌面通知,且不需要 Socket 实时推送
|
||||||
|
if (isMobile) {
|
||||||
|
const emptyValue = {
|
||||||
|
notifications: [],
|
||||||
|
isConnected: false,
|
||||||
|
soundEnabled: false,
|
||||||
|
browserPermission: 'default',
|
||||||
|
connectionStatus: CONNECTION_STATUS.DISCONNECTED,
|
||||||
|
reconnectAttempt: 0,
|
||||||
|
maxReconnectAttempts: 0,
|
||||||
|
addNotification: () => null,
|
||||||
|
removeNotification: () => {},
|
||||||
|
clearAllNotifications: () => {},
|
||||||
|
toggleSound: () => {},
|
||||||
|
requestBrowserPermission: () => Promise.resolve('default'),
|
||||||
|
trackNotificationClick: () => {},
|
||||||
|
retryConnection: () => {},
|
||||||
|
showWelcomeGuide: () => {},
|
||||||
|
showCommunityGuide: () => {},
|
||||||
|
showFirstFollowGuide: () => {},
|
||||||
|
registerEventUpdateCallback: () => () => {},
|
||||||
|
unregisterEventUpdateCallback: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NotificationContext.Provider value={emptyValue}>
|
||||||
|
{children}
|
||||||
|
</NotificationContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 桌面端:完整通知功能 ==========
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const [notifications, setNotifications] = useState([]);
|
const [notifications, setNotifications] = useState([]);
|
||||||
const [isConnected, setIsConnected] = useState(false);
|
const [isConnected, setIsConnected] = useState(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user