UNPKG

@oxyhq/services

Version:

Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀

237 lines (225 loc) • 9.18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useSessionSocket = useSessionSocket; var _react = require("react"); var _socket = _interopRequireDefault(require("socket.io-client")); var _sonner = require("../../lib/sonner"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function useSessionSocket({ userId, activeSessionId, currentDeviceId, refreshSessions, logout, clearSessionState, baseURL, onRemoteSignOut, onSessionRemoved }) { const socketRef = (0, _react.useRef)(null); const joinedRoomRef = (0, _react.useRef)(null); // Store callbacks in refs to avoid re-joining when they change const refreshSessionsRef = (0, _react.useRef)(refreshSessions); const logoutRef = (0, _react.useRef)(logout); const clearSessionStateRef = (0, _react.useRef)(clearSessionState); const onRemoteSignOutRef = (0, _react.useRef)(onRemoteSignOut); const onSessionRemovedRef = (0, _react.useRef)(onSessionRemoved); const activeSessionIdRef = (0, _react.useRef)(activeSessionId); const currentDeviceIdRef = (0, _react.useRef)(currentDeviceId); // Update refs when callbacks change (0, _react.useEffect)(() => { refreshSessionsRef.current = refreshSessions; logoutRef.current = logout; clearSessionStateRef.current = clearSessionState; onRemoteSignOutRef.current = onRemoteSignOut; onSessionRemovedRef.current = onSessionRemoved; activeSessionIdRef.current = activeSessionId; currentDeviceIdRef.current = currentDeviceId; }, [refreshSessions, logout, clearSessionState, onRemoteSignOut, onSessionRemoved, activeSessionId, currentDeviceId]); (0, _react.useEffect)(() => { if (!userId || !baseURL) { // Clean up if userId or baseURL becomes invalid if (socketRef.current && joinedRoomRef.current) { socketRef.current.emit('leave', { userId: joinedRoomRef.current }); joinedRoomRef.current = null; } return; } const roomId = `user:${userId}`; // Only create socket if it doesn't exist if (!socketRef.current) { socketRef.current = (0, _socket.default)(baseURL, { transports: ['websocket'] }); } const socket = socketRef.current; // Only join if we haven't already joined this room if (joinedRoomRef.current !== roomId) { // Leave previous room if switching users if (joinedRoomRef.current) { socket.emit('leave', { userId: joinedRoomRef.current }); } socket.emit('join', { userId: roomId }); joinedRoomRef.current = roomId; if (__DEV__) { console.log('Emitting join for room:', roomId); } } // Set up event handlers (only once per socket instance) const handleConnect = () => { if (__DEV__) { console.log('Socket connected:', socket.id); } }; const handleSessionUpdate = async data => { if (__DEV__) { console.log('Received session_update:', data); } const currentActiveSessionId = activeSessionIdRef.current; const currentDeviceId = currentDeviceIdRef.current; // Handle different event types if (data.type === 'session_removed') { // Track removed session if (data.sessionId && onSessionRemovedRef.current) { onSessionRemovedRef.current(data.sessionId); } // If the removed sessionId matches the current activeSessionId, immediately clear state if (data.sessionId === currentActiveSessionId) { if (onRemoteSignOutRef.current) { onRemoteSignOutRef.current(); } else { _sonner.toast.info('You have been signed out remotely.'); } // Use clearSessionState since session was already removed server-side // Await to ensure storage cleanup completes before continuing try { await clearSessionStateRef.current(); } catch (error) { if (__DEV__) { console.error('Failed to clear session state after session_removed:', error); } } } else { // Otherwise, just refresh the sessions list (with error handling) refreshSessionsRef.current().catch(error => { // Silently handle errors from refresh - they're expected if sessions were removed if (__DEV__) { console.debug('Failed to refresh sessions after session_removed:', error); } }); } } else if (data.type === 'device_removed') { // Track all removed sessions from this device if (data.sessionIds && onSessionRemovedRef.current) { for (const sessionId of data.sessionIds) { onSessionRemovedRef.current(sessionId); } } // If the removed deviceId matches the current device, immediately clear state if (data.deviceId && data.deviceId === currentDeviceId) { if (onRemoteSignOutRef.current) { onRemoteSignOutRef.current(); } else { _sonner.toast.info('This device has been removed. You have been signed out.'); } // Use clearSessionState since sessions were already removed server-side // Await to ensure storage cleanup completes before continuing try { await clearSessionStateRef.current(); } catch (error) { if (__DEV__) { console.error('Failed to clear session state after device_removed:', error); } } } else { // Otherwise, refresh sessions and device list (with error handling) refreshSessionsRef.current().catch(error => { // Silently handle errors from refresh - they're expected if sessions were removed if (__DEV__) { console.debug('Failed to refresh sessions after device_removed:', error); } }); } } else if (data.type === 'sessions_removed') { // Track all removed sessions if (data.sessionIds && onSessionRemovedRef.current) { for (const sessionId of data.sessionIds) { onSessionRemovedRef.current(sessionId); } } // If the current activeSessionId is in the removed sessionIds list, immediately clear state if (data.sessionIds && currentActiveSessionId && data.sessionIds.includes(currentActiveSessionId)) { if (onRemoteSignOutRef.current) { onRemoteSignOutRef.current(); } else { _sonner.toast.info('You have been signed out remotely.'); } // Use clearSessionState since sessions were already removed server-side // Await to ensure storage cleanup completes before continuing try { await clearSessionStateRef.current(); } catch (error) { if (__DEV__) { console.error('Failed to clear session state after sessions_removed:', error); } } } else { // Otherwise, refresh sessions list (with error handling) refreshSessionsRef.current().catch(error => { // Silently handle errors from refresh - they're expected if sessions were removed if (__DEV__) { console.debug('Failed to refresh sessions after sessions_removed:', error); } }); } } else { // For other event types (e.g., session_created), refresh sessions (with error handling) refreshSessionsRef.current().catch(error => { // Log but don't throw - refresh errors shouldn't break the socket handler if (__DEV__) { console.debug('Failed to refresh sessions after session_update:', error); } }); // If the current session was logged out (legacy behavior), handle it specially if (data.sessionId === currentActiveSessionId) { if (onRemoteSignOutRef.current) { onRemoteSignOutRef.current(); } else { _sonner.toast.info('You have been signed out remotely.'); } // Use clearSessionState since session was already removed server-side // Await to ensure storage cleanup completes before continuing try { await clearSessionStateRef.current(); } catch (error) { if (__DEV__) { console.error('Failed to clear session state after session_update:', error); } } } } }; socket.on('connect', handleConnect); socket.on('session_update', handleSessionUpdate); return () => { socket.off('connect', handleConnect); socket.off('session_update', handleSessionUpdate); // Only leave on unmount if we're still in this room if (joinedRoomRef.current === roomId) { socket.emit('leave', { userId: roomId }); joinedRoomRef.current = null; } }; }, [userId, baseURL]); // Only depend on userId and baseURL - callbacks are in refs } //# sourceMappingURL=useSessionSocket.js.map