UNPKG

realtimecursor

Version:

Real-time collaboration system with cursor tracking and approval workflow

123 lines (105 loc) 3.48 kB
import { useState, useEffect, useRef } from 'react'; import RealtimeCursor, { RealtimeCursorOptions, CollaboratorInfo } from './realtimecursor'; export interface UseRealtimeCursorOptions extends RealtimeCursorOptions { autoConnect?: boolean; } export function useRealtimeCursor(options: UseRealtimeCursorOptions) { const [collaborators, setCollaborators] = useState<CollaboratorInfo[]>([]); const [cursors, setCursors] = useState<Record<string, any>>({}); const [typingUsers, setTypingUsers] = useState<string[]>([]); const [isConnected, setIsConnected] = useState<boolean>(false); const cursorClientRef = useRef<RealtimeCursor | null>(null); useEffect(() => { // Create the RealtimeCursor instance const cursorClient = new RealtimeCursor(options); cursorClientRef.current = cursorClient; // Set up event handlers cursorClient.onCollaboratorsChange = (newCollaborators) => { setCollaborators(newCollaborators); }; cursorClient.onCursorUpdate = ({ socketId, user, x, y, textPosition }) => { setCursors((prev: Record<string, any>) => ({ ...prev, [socketId]: { x, y, user, textPosition } })); }; cursorClient.onCursorPositionUpdate = ({ socketId, textPosition, user }) => { setCursors((prev: Record<string, any>) => ({ ...prev, [socketId]: { ...prev[socketId], textPosition, user } })); }; cursorClient.onUserLeft = ({ socketId }) => { setCursors((prev: Record<string, any>) => { const newCursors = { ...prev }; delete newCursors[socketId]; return newCursors; }); }; cursorClient.onTypingStatusChange = (typingUserIds) => { setTypingUsers(typingUserIds); }; // Auto-connect if enabled if (options.autoConnect !== false) { cursorClient.connect(); setIsConnected(true); } // Clean up on unmount return () => { cursorClient.disconnect(); setIsConnected(false); }; }, [options.projectId, options.user.id]); // Reconnect if project or user changes // Connect function const connect = () => { if (cursorClientRef.current) { cursorClientRef.current.connect(); setIsConnected(true); } }; // Disconnect function const disconnect = () => { if (cursorClientRef.current) { cursorClientRef.current.disconnect(); setIsConnected(false); } }; // Update cursor position const updateCursor = (position: { x: number; y: number; textPosition?: number }) => { if (cursorClientRef.current) { cursorClientRef.current.updateCursor(position); } }; // Update cursor position in text const updateCursorPosition = (textPosition: number) => { if (cursorClientRef.current) { cursorClientRef.current.updateCursorPosition(textPosition); } }; // Update content const updateContent = (content: string, cursorPosition?: number) => { if (cursorClientRef.current) { cursorClientRef.current.updateContent(content, cursorPosition); } }; // Set typing indicator const setTyping = (isTyping: boolean) => { if (cursorClientRef.current) { cursorClientRef.current.setTyping(isTyping); } }; return { collaborators, cursors, typingUsers, isConnected, connect, disconnect, updateCursor, updateCursorPosition, updateContent, setTyping, client: cursorClientRef.current }; } export default useRealtimeCursor;