import { useEffect, useRef, useState } from "react"; import { io, type Socket } from "socket.io-client"; import type { BoardState } from "./types"; import { getModeratorToken, setModeratorToken } from "./identity"; export type BoardStatus = "connecting" | "ready" | "notfound" | "destroyed"; export function useBoard(boardId: string, participantId: string, name: string | null) { const [state, setState] = useState(null); const [status, setStatus] = useState("connecting"); const socketRef = useRef(null); useEffect(() => { if (!name) return; const socket = io({ path: "/socket.io" }); socketRef.current = socket; socket.on("connect", () => socket.emit("board:join", { boardId, participantId, name, moderatorToken: getModeratorToken(boardId) }), ); socket.on("board:state", (s: BoardState) => { setState(s); setStatus("ready"); }); socket.on("board:claimed", (p: { moderatorToken: string }) => { setModeratorToken(boardId, p.moderatorToken); }); socket.on("error", (e: { message: string }) => { if (e.message === "board_not_found") setStatus("notfound"); }); socket.on("board:destroyed", () => setStatus("destroyed")); return () => { socket.disconnect(); socketRef.current = null; }; }, [boardId, participantId, name]); const mod = () => getModeratorToken(boardId); const emit = (event: string, payload: Record = {}) => socketRef.current?.emit(event, payload); const modEmit = (event: string, payload: Record = {}) => emit(event, { ...payload, moderatorToken: mod() }); return { state, status, isModerator: state?.youAreModerator ?? false, actions: { // notas addNote: (columnId: string, text: string, color?: string) => emit("note:add", { columnId, text, color }), updateNote: (noteId: string, text: string) => emit("note:update", { noteId, text }), deleteNote: (noteId: string) => emit("note:delete", { noteId }), revealNote: (noteId: string, revealed: boolean) => emit("note:reveal", { noteId, revealed }), publishMine: (columnId: string) => emit("note:publishMine", { columnId }), recolorMine: (color: string) => emit("note:recolorMine", { color }), moveNote: (noteId: string, columnId: string, groupId: string | null, position: number) => emit("note:move", { noteId, columnId, groupId, position }), // grupos createGroup: (noteId: string, targetNoteId: string) => emit("group:create", { noteId, targetNoteId }), renameGroup: (groupId: string, title: string) => emit("group:rename", { groupId, title }), moveGroup: (groupId: string, columnId: string, position: number) => emit("group:move", { groupId, columnId, position }), deleteGroup: (groupId: string) => emit("group:delete", { groupId }), // votos addVote: (targetType: "note" | "group", targetId: string) => emit("vote:add", { targetType, targetId }), removeVote: (targetId: string) => emit("vote:remove", { targetId }), // ajustes y moderación updateBoard: (patch: { title?: string; votesPerUser?: number; allowMultiVote?: boolean; moderationMode?: "everyone" | "single"; showCardCreators?: boolean; locked?: boolean; }) => modEmit("board:update", patch), reveal: (revealed: boolean) => modEmit("board:reveal", { revealed }), revealVotes: (revealed: boolean) => modEmit("board:revealVotes", { revealed }), setPhase: (phase: string) => modEmit("board:phase", { phase }), // columnas (moderador) addColumn: (title: string) => modEmit("column:add", { title }), renameColumn: (columnId: string, title: string) => modEmit("column:rename", { columnId, title }), deleteColumn: (columnId: string) => modEmit("column:delete", { columnId }), reorderColumns: (orderedIds: string[]) => modEmit("column:reorder", { orderedIds }), startTimer: (seconds: number) => modEmit("timer:start", { seconds }), stopTimer: () => modEmit("timer:stop"), claim: () => modEmit("board:claim"), clearVotes: () => modEmit("votes:clear"), removeBoard: () => modEmit("board:remove"), // participante renameMe: (name: string) => emit("participant:rename", { name }), // action items addAction: (text: string) => emit("actionitem:add", { text }), updateAction: (id: string, patch: { text?: string; owner?: string | null }) => emit("actionitem:update", { id, ...patch }), toggleAction: (id: string, done: boolean) => emit("actionitem:toggle", { id, done }), deleteAction: (id: string) => emit("actionitem:delete", { id }), }, }; } export type BoardActions = ReturnType["actions"];