import { useEffect, useRef, useState } from 'react'; import { io, Socket } from 'socket.io-client'; interface CursorPosition { x: number; y: number; } interface PeerMouseHook { cursors: Record; } export function usePeerMouse(roomId: string): PeerMouseHook { const [cursors, setCursors] = useState>({}); const socketRef = useRef(null); const peers = useRef>({}); const channels = useRef>({}); useEffect(() => { const socket = io(); socketRef.current = socket; socket.emit('join-room', roomId); socket.on('new-peer', async (peerId: string) => { const pc = createPeer(peerId, true); const offer = await pc.createOffer(); await pc.setLocalDescription(offer); socket.emit('signal', { to: peerId, data: { description: pc.localDescription } }); }); socket.on('signal', async ({ from, data }) => { const pc = peers.current[from] || createPeer(from, false); if (data.description) { await pc.setRemoteDescription(data.description); if (data.description.type === 'offer') { const answer = await pc.createAnswer(); await pc.setLocalDescription(answer); socket.emit('signal', { to: from, data: { description: pc.localDescription } }); } } if (data.candidate) { await pc.addIceCandidate(data.candidate); } }); function createPeer(peerId: string, isInitiator: boolean): RTCPeerConnection { const pc = new RTCPeerConnection(); if (isInitiator) { const channel = pc.createDataChannel('mouse'); setupChannel(peerId, channel); } else { pc.ondatachannel = (e) => setupChannel(peerId, e.channel); } pc.onicecandidate = (e) => { if (e.candidate) { socket.emit('signal', { to: peerId, data: { candidate: e.candidate } }); } }; peers.current[peerId] = pc; return pc; } function setupChannel(peerId: string, channel: RTCDataChannel) { channels.current[peerId] = channel; channel.onmessage = (e) => { const pos = JSON.parse(e.data); setCursors((prev) => ({ ...prev, [peerId]: pos })); }; } function handleMouseMove(e: MouseEvent) { const pos = JSON.stringify({ x: e.clientX, y: e.clientY }); Object.values(channels.current).forEach((ch) => { if (ch.readyState === 'open') ch.send(pos); }); } window.addEventListener('mousemove', handleMouseMove); return () => { window.removeEventListener('mousemove', handleMouseMove); socket.disconnect(); Object.values(peers.current).forEach((pc) => pc.close()); }; }, [roomId]); return { cursors }; }