/** * AeThex Provider * Main context provider for AeThex Connect - handles auth, chat, and real-time features */ import React, { createContext, useContext, useState, useEffect, useCallback } from 'react'; import { io } from 'socket.io-client'; const API_URL = import.meta.env?.VITE_API_URL || 'http://localhost:5000'; const API_BASE = `${API_URL}/api`; const AeThexContext = createContext(null); export function useAeThex() { return useContext(AeThexContext); } export function AeThexProvider({ children }) { // Auth state const [user, setUser] = useState(null); const [token, setTokenState] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Socket state const [socket, setSocket] = useState(null); const [connected, setConnected] = useState(false); // Chat state const [servers, setServers] = useState([]); const [channels, setChannels] = useState([]); const [messages, setMessages] = useState([]); const [currentServer, setCurrentServer] = useState(null); const [currentChannel, setCurrentChannel] = useState(null); const [onlineUsers, setOnlineUsers] = useState([]); // Token management const setToken = useCallback((newToken) => { setTokenState(newToken); if (newToken) { localStorage.setItem('aethex_token', newToken); } else { localStorage.removeItem('aethex_token'); } }, []); // API request helper const apiRequest = useCallback(async (endpoint, options = {}) => { const headers = { 'Content-Type': 'application/json', ...options.headers, }; const currentToken = token || localStorage.getItem('aethex_token'); if (currentToken) { headers['Authorization'] = `Bearer ${currentToken}`; } const response = await fetch(`${API_BASE}${endpoint}`, { ...options, headers, }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Request failed'); } return data; }, [token]); // Auth functions const login = useCallback(async (email, password) => { setError(null); setLoading(true); try { const response = await apiRequest('/auth/login', { method: 'POST', body: JSON.stringify({ email, password }), }); if (response.success) { setToken(response.data.token); setUser(response.data.user); return { success: true }; } throw new Error(response.error || 'Login failed'); } catch (err) { setError(err.message); return { success: false, error: err.message }; } finally { setLoading(false); } }, [apiRequest, setToken]); const register = useCallback(async (email, password, username, displayName) => { setError(null); setLoading(true); try { const response = await apiRequest('/auth/register', { method: 'POST', body: JSON.stringify({ email, password, username, displayName }), }); if (response.success) { setToken(response.data.token); setUser(response.data.user); return { success: true }; } throw new Error(response.error || 'Registration failed'); } catch (err) { setError(err.message); return { success: false, error: err.message }; } finally { setLoading(false); } }, [apiRequest, setToken]); const demoLogin = useCallback(async () => { setError(null); setLoading(true); try { const response = await apiRequest('/auth/demo', { method: 'POST', }); if (response.success) { setToken(response.data.token); setUser(response.data.user); return { success: true }; } throw new Error(response.error || 'Demo login failed'); } catch (err) { setError(err.message); return { success: false, error: err.message }; } finally { setLoading(false); } }, [apiRequest, setToken]); const logout = useCallback(() => { setToken(null); setUser(null); if (socket) { socket.disconnect(); setSocket(null); } setConnected(false); }, [socket, setToken]); // Socket connection const connectSocket = useCallback((authToken) => { if (socket) { socket.disconnect(); } const newSocket = io(API_URL, { auth: { token: authToken }, reconnection: true, reconnectionDelay: 1000, reconnectionAttempts: 10, transports: ['websocket', 'polling'] }); newSocket.on('connect', () => { console.log('✓ Connected to AeThex Connect'); setConnected(true); }); newSocket.on('disconnect', () => { console.log('✗ Disconnected from AeThex Connect'); setConnected(false); }); newSocket.on('message:new', (message) => { setMessages(prev => [...prev, message]); }); newSocket.on('presence:online', (users) => { setOnlineUsers(users); }); setSocket(newSocket); return newSocket; }, [socket]); // Chat functions const sendMessage = useCallback((content) => { if (!socket || !connected || !currentChannel) return; socket.emit('channel:message', { channelId: currentChannel.id, content }); }, [socket, connected, currentChannel]); const joinChannel = useCallback((channelId) => { if (!socket || !connected) return; socket.emit('channel:join', { channelId }); }, [socket, connected]); // Check auth on mount useEffect(() => { const checkAuth = async () => { const storedToken = localStorage.getItem('aethex_token'); if (storedToken) { setTokenState(storedToken); try { const response = await fetch(`${API_BASE}/auth/me`, { headers: { 'Authorization': `Bearer ${storedToken}` } }); const data = await response.json(); if (data.success) { setUser(data.data); connectSocket(storedToken); } else { localStorage.removeItem('aethex_token'); } } catch (err) { console.error('Auth check failed:', err); localStorage.removeItem('aethex_token'); } } setLoading(false); }; checkAuth(); return () => { if (socket) { socket.disconnect(); } }; }, []); // Connect socket when user logs in useEffect(() => { if (user && token && !socket) { connectSocket(token); } }, [user, token]); const value = { // Auth user, loading, error, isAuthenticated: !!user, login, register, demoLogin, logout, // Socket socket, connected, // Chat servers, channels, messages, currentServer, currentChannel, onlineUsers, setCurrentServer, setCurrentChannel, setMessages, sendMessage, joinChannel, // API helper apiRequest }; return ( {children} ); }