Add frontend alerts imporvements
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { createContext, useContext, useEffect, useRef, useState, ReactNode } from 'react';
|
||||
import React, { createContext, useContext, useEffect, useRef, useState, ReactNode, useCallback } from 'react';
|
||||
import { useAuthStore } from '../stores/auth.store';
|
||||
import { useCurrentTenant } from '../stores/tenant.store';
|
||||
import { showToast } from '../utils/toast';
|
||||
@@ -103,6 +103,11 @@ export const SSEProvider: React.FC<SSEProviderProps> = ({ children }) => {
|
||||
setIsConnected(true);
|
||||
reconnectAttempts.current = 0;
|
||||
|
||||
// Clear processed event IDs on new connection to allow fresh state from server
|
||||
// This ensures events are processed again after reconnection or navigation
|
||||
processedEventIdsRef.current.clear();
|
||||
console.log('🔄 [SSE] Cleared processed event IDs cache on connection open');
|
||||
|
||||
if (reconnectTimeoutRef.current) {
|
||||
clearTimeout(reconnectTimeoutRef.current);
|
||||
reconnectTimeoutRef.current = undefined;
|
||||
@@ -214,6 +219,11 @@ export const SSEProvider: React.FC<SSEProviderProps> = ({ children }) => {
|
||||
// Trigger listeners with enriched alert data
|
||||
// Wrap in queueMicrotask to prevent setState during render warnings
|
||||
const listeners = eventListenersRef.current.get('alert');
|
||||
console.log('📤 [SSEContext] Notifying alert listeners:', {
|
||||
listenerCount: listeners?.size || 0,
|
||||
eventId: data.id,
|
||||
eventType: data.event_type || data.type,
|
||||
});
|
||||
if (listeners) {
|
||||
listeners.forEach(callback => {
|
||||
queueMicrotask(() => callback(data));
|
||||
@@ -347,6 +357,22 @@ export const SSEProvider: React.FC<SSEProviderProps> = ({ children }) => {
|
||||
eventSource.addEventListener('recommendation', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
// GLOBAL DEDUPLICATION: Skip if this event was already processed
|
||||
if (data.id && processedEventIdsRef.current.has(data.id)) {
|
||||
console.log('⏭️ [SSE] Skipping duplicate recommendation:', data.id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark event as processed
|
||||
if (data.id) {
|
||||
processedEventIdsRef.current.add(data.id);
|
||||
if (processedEventIdsRef.current.size > 1000) {
|
||||
const firstId = Array.from(processedEventIdsRef.current)[0];
|
||||
processedEventIdsRef.current.delete(firstId);
|
||||
}
|
||||
}
|
||||
|
||||
const sseEvent: SSEEvent = {
|
||||
type: 'recommendation',
|
||||
data,
|
||||
@@ -433,6 +459,11 @@ export const SSEProvider: React.FC<SSEProviderProps> = ({ children }) => {
|
||||
// Trigger listeners with recommendation data
|
||||
// Wrap in queueMicrotask to prevent setState during render warnings
|
||||
const listeners = eventListenersRef.current.get('recommendation');
|
||||
console.log('📤 [SSEContext] Notifying recommendation listeners:', {
|
||||
listenerCount: listeners?.size || 0,
|
||||
eventId: data.id,
|
||||
eventType: data.event_type || data.type,
|
||||
});
|
||||
if (listeners) {
|
||||
listeners.forEach(callback => {
|
||||
queueMicrotask(() => callback(data));
|
||||
@@ -483,13 +514,14 @@ export const SSEProvider: React.FC<SSEProviderProps> = ({ children }) => {
|
||||
reconnectAttempts.current = 0;
|
||||
};
|
||||
|
||||
const addEventListener = (eventType: string, callback: (data: any) => void) => {
|
||||
// Memoize addEventListener to prevent unnecessary effect re-runs in consumers
|
||||
const addEventListener = useCallback((eventType: string, callback: (data: any) => void) => {
|
||||
if (!eventListenersRef.current.has(eventType)) {
|
||||
eventListenersRef.current.set(eventType, new Set());
|
||||
}
|
||||
|
||||
|
||||
eventListenersRef.current.get(eventType)!.add(callback);
|
||||
|
||||
|
||||
// Return cleanup function
|
||||
return () => {
|
||||
const listeners = eventListenersRef.current.get(eventType);
|
||||
@@ -500,7 +532,7 @@ export const SSEProvider: React.FC<SSEProviderProps> = ({ children }) => {
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}, []); // No dependencies - uses only refs
|
||||
|
||||
// Connect when authenticated, disconnect when not or when tenant changes
|
||||
useEffect(() => {
|
||||
@@ -531,6 +563,8 @@ export const SSEProvider: React.FC<SSEProviderProps> = ({ children }) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Context value - consumers should extract only what they need
|
||||
// addEventListener is now stable (wrapped in useCallback)
|
||||
const contextValue: SSEContextType = {
|
||||
isConnected,
|
||||
lastEvent,
|
||||
|
||||
Reference in New Issue
Block a user