feat: 添加消息推送能力,添加新闻催化分析页的合规提示
This commit is contained in:
207
src/contexts/NotificationContext.js
Normal file
207
src/contexts/NotificationContext.js
Normal file
@@ -0,0 +1,207 @@
|
||||
// src/contexts/NotificationContext.js
|
||||
/**
|
||||
* 通知上下文 - 管理实时消息推送和通知显示
|
||||
*/
|
||||
|
||||
import React, { createContext, useContext, useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { logger } from '../utils/logger';
|
||||
import socket, { SOCKET_TYPE } from '../services/socket';
|
||||
import notificationSound from '../assets/sounds/notification.wav';
|
||||
|
||||
// 创建通知上下文
|
||||
const NotificationContext = createContext();
|
||||
|
||||
// 自定义Hook
|
||||
export const useNotification = () => {
|
||||
const context = useContext(NotificationContext);
|
||||
if (!context) {
|
||||
throw new Error('useNotification must be used within a NotificationProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
// 通知提供者组件
|
||||
export const NotificationProvider = ({ children }) => {
|
||||
const [notifications, setNotifications] = useState([]);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [soundEnabled, setSoundEnabled] = useState(true);
|
||||
const audioRef = useRef(null);
|
||||
|
||||
// 初始化音频
|
||||
useEffect(() => {
|
||||
try {
|
||||
audioRef.current = new Audio(notificationSound);
|
||||
audioRef.current.volume = 0.5;
|
||||
} catch (error) {
|
||||
logger.error('NotificationContext', 'Audio initialization failed', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 播放通知音效
|
||||
*/
|
||||
const playNotificationSound = useCallback(() => {
|
||||
if (!soundEnabled || !audioRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 重置音频到开始位置
|
||||
audioRef.current.currentTime = 0;
|
||||
// 播放音频
|
||||
audioRef.current.play().catch(error => {
|
||||
logger.warn('NotificationContext', 'Failed to play notification sound', error);
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('NotificationContext', 'playNotificationSound', error);
|
||||
}
|
||||
}, [soundEnabled]);
|
||||
|
||||
/**
|
||||
* 添加通知到队列
|
||||
* @param {object} notification - 通知对象
|
||||
*/
|
||||
const addNotification = useCallback((notification) => {
|
||||
const newNotification = {
|
||||
id: notification.id || `notif_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||||
type: notification.type || 'info',
|
||||
severity: notification.severity || 'info',
|
||||
title: notification.title || '通知',
|
||||
message: notification.message || '',
|
||||
timestamp: notification.timestamp || Date.now(),
|
||||
autoClose: notification.autoClose !== undefined ? notification.autoClose : 8000,
|
||||
...notification,
|
||||
};
|
||||
|
||||
logger.info('NotificationContext', 'Adding notification', newNotification);
|
||||
|
||||
// 新消息插入到数组开头,最多保留5条
|
||||
setNotifications(prev => {
|
||||
const updated = [newNotification, ...prev];
|
||||
const maxNotifications = 5;
|
||||
|
||||
// 如果超过最大数量,移除最旧的(数组末尾)
|
||||
if (updated.length > maxNotifications) {
|
||||
const removed = updated.slice(maxNotifications);
|
||||
removed.forEach(old => {
|
||||
logger.info('NotificationContext', 'Auto-removing old notification', { id: old.id });
|
||||
});
|
||||
return updated.slice(0, maxNotifications);
|
||||
}
|
||||
|
||||
return updated;
|
||||
});
|
||||
|
||||
// 播放音效
|
||||
playNotificationSound();
|
||||
|
||||
// 自动关闭
|
||||
if (newNotification.autoClose && newNotification.autoClose > 0) {
|
||||
setTimeout(() => {
|
||||
removeNotification(newNotification.id);
|
||||
}, newNotification.autoClose);
|
||||
}
|
||||
|
||||
return newNotification.id;
|
||||
}, [playNotificationSound]);
|
||||
|
||||
/**
|
||||
* 移除通知
|
||||
* @param {string} id - 通知ID
|
||||
*/
|
||||
const removeNotification = useCallback((id) => {
|
||||
logger.info('NotificationContext', 'Removing notification', { id });
|
||||
setNotifications(prev => prev.filter(notif => notif.id !== id));
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 清空所有通知
|
||||
*/
|
||||
const clearAllNotifications = useCallback(() => {
|
||||
logger.info('NotificationContext', 'Clearing all notifications');
|
||||
setNotifications([]);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 切换音效开关
|
||||
*/
|
||||
const toggleSound = useCallback(() => {
|
||||
setSoundEnabled(prev => {
|
||||
const newValue = !prev;
|
||||
logger.info('NotificationContext', 'Sound toggled', { enabled: newValue });
|
||||
return newValue;
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 连接到 Socket 服务
|
||||
useEffect(() => {
|
||||
logger.info('NotificationContext', 'Initializing socket connection...');
|
||||
|
||||
// 连接 socket
|
||||
socket.connect();
|
||||
|
||||
// 监听连接状态
|
||||
socket.on('connect', () => {
|
||||
setIsConnected(true);
|
||||
logger.info('NotificationContext', 'Socket connected');
|
||||
|
||||
// 如果使用 mock,可以启动定期推送
|
||||
if (SOCKET_TYPE === 'MOCK') {
|
||||
// 启动模拟推送:每20秒推送1-2条消息
|
||||
socket.startMockPush(20000, 2);
|
||||
logger.info('NotificationContext', 'Mock push started');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
setIsConnected(false);
|
||||
logger.warn('NotificationContext', 'Socket disconnected');
|
||||
});
|
||||
|
||||
// 监听交易通知
|
||||
socket.on('trade_notification', (data) => {
|
||||
logger.info('NotificationContext', 'Received trade notification', data);
|
||||
addNotification(data);
|
||||
});
|
||||
|
||||
// 监听系统通知
|
||||
socket.on('system_notification', (data) => {
|
||||
logger.info('NotificationContext', 'Received system notification', data);
|
||||
addNotification(data);
|
||||
});
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
logger.info('NotificationContext', 'Cleaning up socket connection');
|
||||
|
||||
// 如果是 mock service,停止推送
|
||||
if (SOCKET_TYPE === 'MOCK') {
|
||||
socket.stopMockPush();
|
||||
}
|
||||
|
||||
socket.off('connect');
|
||||
socket.off('disconnect');
|
||||
socket.off('trade_notification');
|
||||
socket.off('system_notification');
|
||||
socket.disconnect();
|
||||
};
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const value = {
|
||||
notifications,
|
||||
isConnected,
|
||||
soundEnabled,
|
||||
addNotification,
|
||||
removeNotification,
|
||||
clearAllNotifications,
|
||||
toggleSound,
|
||||
};
|
||||
|
||||
return (
|
||||
<NotificationContext.Provider value={value}>
|
||||
{children}
|
||||
</NotificationContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotificationContext;
|
||||
Reference in New Issue
Block a user