UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

217 lines (214 loc) 6.27 kB
import { io } from 'socket.io-client'; import { EventEmitter } from 'events'; class CollaborationService extends EventEmitter { constructor(serverUrl, authToken) { super(); this.serverUrl = serverUrl; this.authToken = authToken; this.socket = null; this.currentRoom = null; this.reconnectAttempts = 0; this.maxReconnectAttempts = 5; this.operationQueue = []; this.isConnected = false; this.presenceMap = new Map(); this.userId = this.generateUserId(); this.userName = 'User'; } async connect(userName) { if (userName) this.userName = userName; return new Promise((resolve, reject) => { this.socket = io(this.serverUrl, { auth: { token: this.authToken, userId: this.userId, userName: this.userName }, reconnection: true, reconnectionDelay: 1000, reconnectionDelayMax: 5000, reconnectionAttempts: this.maxReconnectAttempts }); this.setupEventHandlers(); this.socket.on('connect', () => { this.isConnected = true; this.reconnectAttempts = 0; this.flushOperationQueue(); this.emit('connected'); resolve(); }); this.socket.on('connect_error', error => { this.reconnectAttempts++; if (this.reconnectAttempts >= this.maxReconnectAttempts) { reject(new Error(`Failed to connect: ${error.message}`)); } }); }); } setupEventHandlers() { if (!this.socket) return; this.socket.on('disconnect', () => { this.isConnected = false; this.emit('disconnected'); }); this.socket.on('cursor-update', data => { this.updatePresenceCursor(data); this.emit('cursor-moved', data); }); this.socket.on('operation-applied', operation => { this.emit('document-changed', operation); }); this.socket.on('presence-update', presence => { this.presenceMap.set(presence.userId, presence); this.emit('presence-changed', Array.from(this.presenceMap.values())); }); this.socket.on('user-joined', user => { this.presenceMap.set(user.userId, user); this.emit('user-joined', user); }); this.socket.on('user-left', userId => { this.presenceMap.delete(userId); this.emit('user-left', userId); }); this.socket.on('room-state', state => { this.currentRoom = state.roomId; state.participants.forEach(p => this.presenceMap.set(p.userId, p)); this.emit('room-synced', state); }); this.socket.on('conflict-detected', conflict => { this.emit('conflict', conflict); }); } async joinRoom(roomId) { if (!this.socket || !this.isConnected) { throw new Error('Not connected to collaboration server'); } return new Promise((resolve, reject) => { this.socket.emit('join-room', roomId, response => { if (response.error) { reject(new Error(response.error)); } else { this.currentRoom = roomId; resolve(); } }); }); } async leaveRoom() { if (!this.socket || !this.currentRoom) return; return new Promise(resolve => { this.socket.emit('leave-room', this.currentRoom, () => { this.currentRoom = null; this.presenceMap.clear(); resolve(); }); }); } sendCursorPosition(x, y) { if (!this.socket || !this.isConnected || !this.currentRoom) return; const cursorData = { x, y, userId: this.userId, userName: this.userName, color: this.getUserColor() }; this.socket.emit('cursor-move', cursorData); } sendEdit(edit) { const fullEdit = { ...edit, userId: this.userId, timestamp: Date.now() }; if (!this.socket || !this.isConnected) { this.operationQueue.push(fullEdit); return; } this.socket.emit('collaborative-edit', fullEdit); } updatePresence(status) { if (!this.socket || !this.isConnected) return; const presence = { userId: this.userId, userName: this.userName, status, lastActivity: new Date() }; this.socket.emit('update-presence', presence); } updateSelection(start, end) { if (!this.socket || !this.isConnected) return; this.socket.emit('selection-change', { userId: this.userId, selection: { start, end } }); } async createRoom(initialState) { if (!this.socket || !this.isConnected) { throw new Error('Not connected to collaboration server'); } return new Promise((resolve, reject) => { this.socket.emit('create-room', { initialState }, response => { if (response.error) { reject(new Error(response.error)); } else { resolve(response.roomId); } }); }); } getRoomParticipants() { return Array.from(this.presenceMap.values()); } getParticipantCount() { return this.presenceMap.size; } updatePresenceCursor(cursorData) { const presence = this.presenceMap.get(cursorData.userId); if (presence) { presence.cursor = cursorData; presence.lastActivity = new Date(); this.presenceMap.set(cursorData.userId, presence); } } flushOperationQueue() { if (!this.socket || !this.isConnected) return; while (this.operationQueue.length > 0) { const operation = this.operationQueue.shift(); if (operation) { this.socket.emit('collaborative-edit', operation); } } } generateUserId() { return `user-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } getUserColor() { const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F']; const index = parseInt(this.userId.substr(-2), 36) % colors.length; return colors[index]; } disconnect() { if (this.socket) { this.socket.disconnect(); this.socket = null; this.isConnected = false; this.currentRoom = null; this.presenceMap.clear(); } } isConnectedToServer() { return this.isConnected; } getCurrentRoom() { return this.currentRoom; } } export { CollaborationService }; //# sourceMappingURL=collaboration-service.js.map