mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 16:03:21 +00:00
feat: mise en place de la messagerie [#17]
This commit is contained in:
270
Front-End/src/context/ChatConnectionContext.js
Normal file
270
Front-End/src/context/ChatConnectionContext.js
Normal file
@ -0,0 +1,270 @@
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useState,
|
||||
useEffect,
|
||||
useRef,
|
||||
useCallback,
|
||||
} from 'react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import logger from '@/utils/logger';
|
||||
import { WS_CHAT_URL } from '@/utils/Url';
|
||||
|
||||
const ChatConnectionContext = createContext();
|
||||
|
||||
export const ChatConnectionProvider = ({ children }) => {
|
||||
const { data: session, status } = useSession(); // Ajouter le hook useSession
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [connectionStatus, setConnectionStatus] = useState('disconnected'); // 'disconnected', 'connecting', 'connected', 'error'
|
||||
const [userPresences, setUserPresences] = useState({}); // Nouvel état pour les présences
|
||||
const websocketRef = useRef(null);
|
||||
const reconnectTimeoutRef = useRef(null);
|
||||
const [reconnectAttempts, setReconnectAttempts] = useState(0);
|
||||
const [currentUserId, setCurrentUserId] = useState(null);
|
||||
const maxReconnectAttempts = 5;
|
||||
|
||||
// Système de callbacks pour les messages
|
||||
const messageCallbacksRef = useRef(new Set());
|
||||
|
||||
// Fonctions pour gérer les callbacks de messages
|
||||
const addMessageCallback = useCallback((callback) => {
|
||||
messageCallbacksRef.current.add(callback);
|
||||
return () => {
|
||||
messageCallbacksRef.current.delete(callback);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const notifyMessageCallbacks = useCallback((data) => {
|
||||
messageCallbacksRef.current.forEach((callback) => {
|
||||
try {
|
||||
callback(data);
|
||||
} catch (error) {
|
||||
logger.error('ChatConnection: Error in message callback', error);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Gestion des présences utilisateur
|
||||
const handlePresenceUpdate = useCallback((data) => {
|
||||
const { user_id, status } = data;
|
||||
setUserPresences((prev) => ({
|
||||
...prev,
|
||||
[user_id]: { status },
|
||||
}));
|
||||
}, []);
|
||||
|
||||
// Configuration WebSocket
|
||||
const getWebSocketUrl = (userId) => {
|
||||
if (!userId) {
|
||||
logger.warn('ChatConnection: No user ID provided for WebSocket URL');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Récupérer le token d'authentification depuis NextAuth session
|
||||
const token = session?.user?.token;
|
||||
|
||||
if (!token) {
|
||||
logger.warn(
|
||||
'ChatConnection: No access token found for WebSocket connection'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Construire l'URL WebSocket avec le token
|
||||
const baseUrl = WS_CHAT_URL(userId);
|
||||
const wsUrl = `${baseUrl}?token=${encodeURIComponent(token)}`;
|
||||
|
||||
return wsUrl;
|
||||
};
|
||||
|
||||
// Connexion WebSocket
|
||||
const connectToChat = (userId = null) => {
|
||||
const userIdToUse = userId || currentUserId;
|
||||
|
||||
// Vérifier que la session est chargée
|
||||
if (status === 'loading') {
|
||||
setConnectionStatus('connecting');
|
||||
return;
|
||||
}
|
||||
|
||||
if (status === 'unauthenticated' || !session) {
|
||||
logger.warn('ChatConnection: User not authenticated');
|
||||
setConnectionStatus('error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userIdToUse) {
|
||||
logger.warn('ChatConnection: Cannot connect without user ID');
|
||||
setConnectionStatus('error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (websocketRef.current?.readyState === WebSocket.OPEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentUserId(userIdToUse);
|
||||
setConnectionStatus('connecting');
|
||||
|
||||
try {
|
||||
const wsUrl = getWebSocketUrl(userIdToUse);
|
||||
|
||||
if (!wsUrl) {
|
||||
throw new Error(
|
||||
'Cannot generate WebSocket URL - missing token or user ID'
|
||||
);
|
||||
}
|
||||
|
||||
websocketRef.current = new WebSocket(wsUrl);
|
||||
|
||||
websocketRef.current.onopen = () => {
|
||||
logger.info(
|
||||
'ChatConnection: Connected successfully for user:',
|
||||
userIdToUse
|
||||
);
|
||||
setIsConnected(true);
|
||||
setConnectionStatus('connected');
|
||||
setReconnectAttempts(0);
|
||||
};
|
||||
|
||||
websocketRef.current.onclose = (event) => {
|
||||
setIsConnected(false);
|
||||
setConnectionStatus('disconnected');
|
||||
|
||||
// Tentative de reconnexion automatique
|
||||
if (reconnectAttempts < maxReconnectAttempts && !event.wasClean) {
|
||||
const timeout = Math.min(
|
||||
1000 * Math.pow(2, reconnectAttempts),
|
||||
30000
|
||||
);
|
||||
|
||||
reconnectTimeoutRef.current = setTimeout(() => {
|
||||
setReconnectAttempts((prev) => prev + 1);
|
||||
connectToChat();
|
||||
}, timeout);
|
||||
}
|
||||
};
|
||||
|
||||
websocketRef.current.onerror = (error) => {
|
||||
logger.error('ChatConnection: WebSocket error', error);
|
||||
setConnectionStatus('error');
|
||||
setIsConnected(false);
|
||||
};
|
||||
|
||||
websocketRef.current.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
// Gérer les messages de présence
|
||||
if (data.type === 'presence_update') {
|
||||
handlePresenceUpdate(data);
|
||||
}
|
||||
|
||||
// Notifier tous les callbacks enregistrés
|
||||
notifyMessageCallbacks(data);
|
||||
} catch (error) {
|
||||
logger.error('ChatConnection: Error parsing message', error);
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('ChatConnection: Error creating WebSocket', error);
|
||||
setConnectionStatus('error');
|
||||
setIsConnected(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Déconnexion WebSocket
|
||||
const disconnectFromChat = () => {
|
||||
if (reconnectTimeoutRef.current) {
|
||||
clearTimeout(reconnectTimeoutRef.current);
|
||||
reconnectTimeoutRef.current = null;
|
||||
}
|
||||
|
||||
if (websocketRef.current) {
|
||||
websocketRef.current.close(1000, 'User disconnected');
|
||||
websocketRef.current = null;
|
||||
}
|
||||
|
||||
setIsConnected(false);
|
||||
setConnectionStatus('disconnected');
|
||||
setReconnectAttempts(0);
|
||||
logger.info('ChatConnection: Disconnected by user');
|
||||
};
|
||||
|
||||
// Envoi de message
|
||||
const sendMessage = (message) => {
|
||||
if (websocketRef.current?.readyState === WebSocket.OPEN) {
|
||||
const messageStr = JSON.stringify(message);
|
||||
websocketRef.current.send(messageStr);
|
||||
return true;
|
||||
} else {
|
||||
logger.warn('ChatConnection: Cannot send message - not connected');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Obtenir la référence WebSocket pour les composants qui en ont besoin
|
||||
const getWebSocket = () => websocketRef.current;
|
||||
|
||||
// Effet pour la gestion de la session et connexion automatique
|
||||
useEffect(() => {
|
||||
// Si la session change vers authenticated et qu'on a un user_id, essayer de se connecter
|
||||
if (status === 'authenticated' && session?.user?.user_id && !isConnected) {
|
||||
connectToChat(session.user.user_id);
|
||||
}
|
||||
|
||||
// Si la session devient unauthenticated, déconnecter
|
||||
if (status === 'unauthenticated' && isConnected) {
|
||||
disconnectFromChat();
|
||||
}
|
||||
}, [
|
||||
status,
|
||||
session?.user?.user_id,
|
||||
isConnected,
|
||||
connectToChat,
|
||||
disconnectFromChat,
|
||||
]);
|
||||
|
||||
// Nettoyage à la destruction du composant
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (reconnectTimeoutRef.current) {
|
||||
clearTimeout(reconnectTimeoutRef.current);
|
||||
}
|
||||
if (websocketRef.current) {
|
||||
websocketRef.current.close();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const value = {
|
||||
isConnected,
|
||||
connectionStatus,
|
||||
userPresences, // Ajouter les présences utilisateur
|
||||
connectToChat,
|
||||
disconnectFromChat,
|
||||
sendMessage,
|
||||
getWebSocket,
|
||||
reconnectAttempts,
|
||||
maxReconnectAttempts,
|
||||
addMessageCallback, // Ajouter cette fonction
|
||||
};
|
||||
|
||||
return (
|
||||
<ChatConnectionContext.Provider value={value}>
|
||||
{children}
|
||||
</ChatConnectionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useChatConnection = () => {
|
||||
const context = useContext(ChatConnectionContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'useChatConnection must be used within a ChatConnectionProvider'
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export default ChatConnectionContext;
|
||||
Reference in New Issue
Block a user