187 lines
4.6 KiB
TypeScript
187 lines
4.6 KiB
TypeScript
// Whiteboard WebSocket Store mit Svelte 5 Runes
|
|
import { io, Socket } from 'socket.io-client';
|
|
|
|
export interface Point {
|
|
x: number;
|
|
y: number;
|
|
}
|
|
|
|
export type DrawingTool = 'pen' | 'eraser' | 'line' | 'rectangle' | 'circle' | 'text';
|
|
|
|
export interface DrawingData {
|
|
id: string;
|
|
whiteboardId: string;
|
|
tool: DrawingTool;
|
|
points: Point[];
|
|
color?: string;
|
|
lineWidth?: number;
|
|
text?: string;
|
|
userId: string;
|
|
timestamp: number;
|
|
}
|
|
|
|
export interface RemoteCursor {
|
|
userId: string;
|
|
x: number;
|
|
y: number;
|
|
}
|
|
|
|
class WhiteboardStore {
|
|
socket: Socket | null = null;
|
|
connected = $state(false);
|
|
whiteboardId = $state<string>('default-room');
|
|
userId = $state<string>('');
|
|
activeUsers = $state<string[]>([]);
|
|
remoteCursors = $state<Map<string, RemoteCursor>>(new Map());
|
|
drawings = $state<DrawingData[]>([]);
|
|
|
|
connect(whiteboardId: string, userId: string) {
|
|
this.whiteboardId = whiteboardId;
|
|
this.userId = userId;
|
|
|
|
if (this.socket) return;
|
|
|
|
this.socket = io(`http://${window.location.hostname}:3000/whiteboard`, {
|
|
withCredentials: true,
|
|
transports: ['websocket', 'polling']
|
|
});
|
|
|
|
this.setupEventListeners();
|
|
|
|
// Dem Whiteboard beitreten
|
|
this.socket.emit('join-whiteboard', {
|
|
whiteboardId,
|
|
userId
|
|
});
|
|
}
|
|
|
|
private setupEventListeners() {
|
|
if (!this.socket) return;
|
|
|
|
this.socket.on('connect', () => {
|
|
console.log('✅ Connected to whiteboard');
|
|
this.connected = true;
|
|
});
|
|
|
|
this.socket.on('disconnect', () => {
|
|
console.log('❌ Disconnected from whiteboard');
|
|
this.connected = false;
|
|
});
|
|
|
|
// Empfange aktuelle Whiteboard-Daten
|
|
this.socket.on('whiteboard-state', (drawings: DrawingData[]) => {
|
|
console.log('📥 Received whiteboard state:', drawings.length, 'drawings');
|
|
this.drawings = drawings;
|
|
});
|
|
|
|
// Empfange neue Zeichnungen von anderen Benutzern
|
|
this.socket.on('drawing', (drawing: DrawingData) => {
|
|
console.log('🎨 Received drawing from user:', drawing.userId);
|
|
this.drawings = [...this.drawings, drawing];
|
|
});
|
|
|
|
// Benutzer ist beigetreten
|
|
this.socket.on('user-joined', (data: { userId: string; activeUsers: string[] }) => {
|
|
console.log('👋 User joined:', data.userId);
|
|
this.activeUsers = data.activeUsers;
|
|
});
|
|
|
|
// Benutzer hat verlassen
|
|
this.socket.on('user-left', (data: { userId: string; activeUsers: string[] }) => {
|
|
console.log('👋 User left:', data.userId);
|
|
this.activeUsers = data.activeUsers;
|
|
// Entferne Cursor des Benutzers
|
|
this.remoteCursors.delete(data.userId);
|
|
});
|
|
|
|
// Whiteboard wurde gelöscht
|
|
this.socket.on('whiteboard-cleared', (data: { userId: string }) => {
|
|
console.log('🗑️ Whiteboard cleared by:', data.userId);
|
|
this.drawings = [];
|
|
});
|
|
|
|
// Cursor-Position von anderen Benutzern
|
|
this.socket.on('cursor-position', (data: { userId: string; x: number; y: number }) => {
|
|
this.remoteCursors.set(data.userId, {
|
|
userId: data.userId,
|
|
x: data.x,
|
|
y: data.y
|
|
});
|
|
// Trigger reaktivität
|
|
this.remoteCursors = new Map(this.remoteCursors);
|
|
});
|
|
|
|
// Undo wurde angefordert
|
|
this.socket.on('undo-requested', (data: { userId: string }) => {
|
|
console.log('↩️ Undo requested by:', data.userId);
|
|
// Hier könnte man Undo-Logik implementieren
|
|
});
|
|
}
|
|
|
|
sendDrawing(tool: DrawingTool, points: Point[], color: string, lineWidth: number, text?: string, id?: string) {
|
|
if (!this.socket || !this.connected) {
|
|
console.error('❌ Not connected to whiteboard');
|
|
return;
|
|
}
|
|
|
|
const drawingData: DrawingData = {
|
|
id: id || `${Date.now()}-${Math.random()}`,
|
|
whiteboardId: this.whiteboardId,
|
|
tool,
|
|
points,
|
|
color,
|
|
lineWidth,
|
|
text,
|
|
userId: this.userId,
|
|
timestamp: Date.now()
|
|
};
|
|
|
|
console.log('📤 Sending drawing:', tool, 'points:', points.length);
|
|
|
|
// Füge die Zeichnung sofort lokal hinzu (optimistische Update)
|
|
this.drawings = [...this.drawings, drawingData];
|
|
|
|
// Sende an den Server (wird nur an andere Clients weitergeleitet)
|
|
this.socket.emit('draw', drawingData);
|
|
}
|
|
|
|
clearWhiteboard() {
|
|
if (!this.socket || !this.connected) {
|
|
console.error('❌ Not connected to whiteboard');
|
|
return;
|
|
}
|
|
|
|
this.socket.emit('clear-whiteboard', {
|
|
whiteboardId: this.whiteboardId,
|
|
userId: this.userId
|
|
});
|
|
|
|
// Lösche lokal
|
|
this.drawings = [];
|
|
}
|
|
|
|
sendCursorPosition(x: number, y: number) {
|
|
if (!this.socket || !this.connected) return;
|
|
|
|
this.socket.emit('cursor-move', {
|
|
whiteboardId: this.whiteboardId,
|
|
userId: this.userId,
|
|
x,
|
|
y
|
|
});
|
|
}
|
|
|
|
disconnect() {
|
|
if (this.socket) {
|
|
this.socket.disconnect();
|
|
this.socket = null;
|
|
}
|
|
this.connected = false;
|
|
this.activeUsers = [];
|
|
this.remoteCursors = new Map();
|
|
this.drawings = [];
|
|
}
|
|
}
|
|
|
|
export const whiteboardStore = new WhiteboardStore();
|