feat(frontend): Implement SignalR client integration for real-time notifications
Add comprehensive SignalR client implementation with connection management, React hooks, and UI components for real-time notifications and project updates. Changes: - Install @microsoft/signalr package (v9.0.6) - Create SignalR connection manager with auto-reconnect - Implement useNotificationHub hook for notification hub - Implement useProjectHub hook for project hub with room-based subscriptions - Add NotificationPopover UI component with badge and dropdown - Create Badge UI component - Add SignalRProvider for global connection initialization - Update Header to display real-time notifications - Update app layout to include SignalRProvider - Add comprehensive documentation in SIGNALR_INTEGRATION.md Features: - JWT authentication with automatic token management - Auto-reconnect with exponential backoff (0s, 2s, 5s, 10s, 30s) - Connection state management and indicators - Real-time notification push - Project event subscriptions (create, update, delete, status change) - Room-based project subscriptions - Typing indicators support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
79
lib/hooks/useNotificationHub.ts
Normal file
79
lib/hooks/useNotificationHub.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import { SignalRConnectionManager } from '@/lib/signalr/ConnectionManager';
|
||||
import { SIGNALR_CONFIG } from '@/lib/signalr/config';
|
||||
import { useAuthStore } from '@/stores/authStore';
|
||||
|
||||
export interface Notification {
|
||||
message: string;
|
||||
type: 'info' | 'success' | 'warning' | 'error' | 'test';
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export function useNotificationHub() {
|
||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
||||
const [connectionState, setConnectionState] = useState<
|
||||
'disconnected' | 'connecting' | 'connected' | 'reconnecting'
|
||||
>('disconnected');
|
||||
const [notifications, setNotifications] = useState<Notification[]>([]);
|
||||
const managerRef = useRef<SignalRConnectionManager | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated) return;
|
||||
|
||||
const manager = new SignalRConnectionManager(
|
||||
SIGNALR_CONFIG.HUB_URLS.NOTIFICATION
|
||||
);
|
||||
managerRef.current = manager;
|
||||
|
||||
// 监听连接状态
|
||||
const unsubscribe = manager.onStateChange(setConnectionState);
|
||||
|
||||
// 监听通知事件
|
||||
manager.on('Notification', (notification: Notification) => {
|
||||
console.log('[NotificationHub] Received notification:', notification);
|
||||
setNotifications((prev) => [notification, ...prev].slice(0, 50)); // 保留最近 50 条
|
||||
});
|
||||
|
||||
manager.on(
|
||||
'NotificationRead',
|
||||
(data: { NotificationId: string; ReadAt: string }) => {
|
||||
console.log('[NotificationHub] Notification read:', data);
|
||||
}
|
||||
);
|
||||
|
||||
// 启动连接
|
||||
manager.start();
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
manager.stop();
|
||||
};
|
||||
}, [isAuthenticated]);
|
||||
|
||||
const markAsRead = useCallback(async (notificationId: string) => {
|
||||
if (!managerRef.current) return;
|
||||
|
||||
try {
|
||||
await managerRef.current.invoke('MarkAsRead', notificationId);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'[NotificationHub] Error marking notification as read:',
|
||||
error
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const clearNotifications = useCallback(() => {
|
||||
setNotifications([]);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
connectionState,
|
||||
notifications,
|
||||
markAsRead,
|
||||
clearNotifications,
|
||||
isConnected: connectionState === 'connected',
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user