2026-04-23 22:09:25 -07:00
|
|
|
import { useEffect, type MutableRefObject, type RefObject } from "react";
|
|
|
|
|
import { toast } from "sonner";
|
|
|
|
|
import { getTeamForPick } from "~/lib/draft-order";
|
2026-06-06 08:52:04 -07:00
|
|
|
import { clientExpiresAt } from "~/lib/draft-timer";
|
2026-04-23 22:09:25 -07:00
|
|
|
import { getAutodraftLabel } from "~/components/AutodraftSettings";
|
|
|
|
|
import type { getDraftPicksForSeason } from "~/models/draft-pick";
|
|
|
|
|
import type { findDraftSlotsBySeasonId } from "~/models/draft-slot";
|
|
|
|
|
import type { QueueItem, AutodraftStatusEntry } from "~/hooks/useDraftRoomState";
|
|
|
|
|
|
|
|
|
|
type DraftPicks = Awaited<ReturnType<typeof getDraftPicksForSeason>>;
|
|
|
|
|
type DraftSlots = Awaited<ReturnType<typeof findDraftSlotsBySeasonId>>;
|
|
|
|
|
type DraftPick = DraftPicks[number];
|
|
|
|
|
|
|
|
|
|
interface UseDraftSocketEventsParams {
|
|
|
|
|
on: (event: string, handler: (data: unknown) => void) => void;
|
|
|
|
|
off: (event: string, handler: (data: unknown) => void) => void;
|
|
|
|
|
socketVersion: number;
|
|
|
|
|
// Loader-derived, stable for lifetime of page load
|
|
|
|
|
userTeam: { id: string } | undefined;
|
|
|
|
|
draftSlots: DraftSlots;
|
|
|
|
|
leagueName: string;
|
|
|
|
|
// Refs from useDraftAuthRecovery
|
|
|
|
|
isRevalidatingRef: RefObject<boolean>;
|
|
|
|
|
pendingPicksDuringRevalidationRef: MutableRefObject<DraftPicks>;
|
|
|
|
|
pendingQueueMutationsRef: MutableRefObject<number>;
|
|
|
|
|
// Notification refs from the component
|
|
|
|
|
sendNotificationRef: MutableRefObject<(title: string, body: string) => void>;
|
|
|
|
|
notificationsModeRef: MutableRefObject<string>;
|
|
|
|
|
// State setters
|
|
|
|
|
setPicks: (fn: (prev: DraftPicks) => DraftPicks) => void;
|
|
|
|
|
setCurrentPick: (pick: number) => void;
|
|
|
|
|
setIsPaused: (paused: boolean) => void;
|
|
|
|
|
setIsDraftComplete: (complete: boolean) => void;
|
|
|
|
|
setAutodraftStatus: (fn: (prev: Record<string, AutodraftStatusEntry>) => Record<string, AutodraftStatusEntry>) => void;
|
|
|
|
|
setUserAutodraft: (value: { isEnabled: boolean; mode: "next_pick" | "while_on"; queueOnly: boolean }) => void;
|
|
|
|
|
setConnectedTeams: (fn: (prev: Set<string>) => Set<string>) => void;
|
|
|
|
|
setQueue: (fn: (prev: QueueItem[]) => QueueItem[]) => void;
|
|
|
|
|
setTeamTimers: (fn: (prev: Record<string, number>) => Record<string, number>) => void;
|
2026-06-06 05:57:46 +00:00
|
|
|
setPickTimerExpiresAt: (state: { teamId: string; expiresAt: number } | null) => void;
|
2026-04-26 22:31:52 -07:00
|
|
|
setIsOvernightPause: (value: boolean) => void;
|
|
|
|
|
setOvernightResumesAt: (value: Date | null) => void;
|
2026-04-29 11:49:26 -07:00
|
|
|
setWatchedParticipantIds: (value: Set<string>) => void;
|
2026-04-30 10:14:14 -07:00
|
|
|
setRoomClosed: (value: boolean) => void;
|
2026-04-30 20:33:00 -07:00
|
|
|
setIsSyncing: (value: boolean) => void;
|
2026-05-04 20:31:44 -07:00
|
|
|
setBracktVorps: (fn: (prev: Map<string, number>) => Map<string, number>) => void;
|
2026-05-10 23:17:29 -07:00
|
|
|
setAnimatingOutParticipantId: (id: string) => void;
|
2026-04-23 22:09:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useDraftSocketEvents({
|
|
|
|
|
on,
|
|
|
|
|
off,
|
|
|
|
|
socketVersion,
|
|
|
|
|
userTeam,
|
|
|
|
|
draftSlots,
|
|
|
|
|
leagueName,
|
|
|
|
|
isRevalidatingRef,
|
|
|
|
|
pendingPicksDuringRevalidationRef,
|
|
|
|
|
pendingQueueMutationsRef,
|
|
|
|
|
sendNotificationRef,
|
|
|
|
|
notificationsModeRef,
|
|
|
|
|
setPicks,
|
|
|
|
|
setCurrentPick,
|
|
|
|
|
setIsPaused,
|
|
|
|
|
setIsDraftComplete,
|
|
|
|
|
setAutodraftStatus,
|
|
|
|
|
setUserAutodraft,
|
|
|
|
|
setConnectedTeams,
|
|
|
|
|
setQueue,
|
|
|
|
|
setTeamTimers,
|
2026-06-06 05:57:46 +00:00
|
|
|
setPickTimerExpiresAt,
|
2026-04-26 22:31:52 -07:00
|
|
|
setIsOvernightPause,
|
|
|
|
|
setOvernightResumesAt,
|
2026-04-29 11:49:26 -07:00
|
|
|
setWatchedParticipantIds,
|
2026-04-30 10:14:14 -07:00
|
|
|
setRoomClosed,
|
2026-04-30 20:33:00 -07:00
|
|
|
setIsSyncing,
|
2026-05-04 20:31:44 -07:00
|
|
|
setBracktVorps,
|
2026-05-10 23:17:29 -07:00
|
|
|
setAnimatingOutParticipantId,
|
2026-04-23 22:09:25 -07:00
|
|
|
}: UseDraftSocketEventsParams) {
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
type PickMadePayload = {
|
|
|
|
|
pick: DraftPick & { team?: { id?: string; name?: string }; participant?: { name?: string } };
|
|
|
|
|
nextPickNumber: number;
|
|
|
|
|
isDraftComplete?: boolean;
|
|
|
|
|
};
|
|
|
|
|
const handlePickMade = (data: PickMadePayload) => {
|
2026-06-06 05:57:46 +00:00
|
|
|
// Stop any running countdown — timer-pick-started for the next team will restart it.
|
|
|
|
|
setPickTimerExpiresAt(null);
|
2026-04-23 22:09:25 -07:00
|
|
|
if (isRevalidatingRef.current) {
|
|
|
|
|
pendingPicksDuringRevalidationRef.current.push(data.pick);
|
|
|
|
|
} else {
|
|
|
|
|
setPicks((prev) => [...prev, data.pick]);
|
|
|
|
|
setCurrentPick(data.nextPickNumber);
|
2026-05-10 23:17:29 -07:00
|
|
|
if (data.pick?.participant?.id) {
|
|
|
|
|
setAnimatingOutParticipantId(data.pick.participant.id);
|
|
|
|
|
}
|
2026-04-23 22:09:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.isDraftComplete) return;
|
|
|
|
|
|
|
|
|
|
const notifTitle = `${leagueName} Draft`;
|
|
|
|
|
const nextSlot = getTeamForPick(data.nextPickNumber, draftSlots);
|
|
|
|
|
const isNowMyTurn = !!(userTeam && nextSlot && nextSlot.team.id === userTeam.id);
|
|
|
|
|
|
|
|
|
|
if (isNowMyTurn) {
|
|
|
|
|
sendNotificationRef.current(notifTitle, `It's your turn to pick!`);
|
|
|
|
|
} else if (
|
|
|
|
|
notificationsModeRef.current === "all_picks" &&
|
|
|
|
|
data.pick?.team?.id !== userTeam?.id
|
|
|
|
|
) {
|
|
|
|
|
const pickerName = data.pick?.team?.name || "A team";
|
|
|
|
|
const participantName = data.pick?.participant?.name || "a participant";
|
|
|
|
|
sendNotificationRef.current(notifTitle, `${pickerName} picked ${participantName}`);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-06 05:57:46 +00:00
|
|
|
const handleTimerPickStarted = (data: {
|
|
|
|
|
seasonId: string;
|
2026-04-26 22:31:52 -07:00
|
|
|
teamId: string;
|
2026-06-06 05:57:46 +00:00
|
|
|
pickNumber: number;
|
|
|
|
|
expiresAt: number;
|
2026-04-26 22:31:52 -07:00
|
|
|
timeRemaining: number;
|
2026-06-06 05:57:46 +00:00
|
|
|
}) => {
|
|
|
|
|
setCurrentPick(data.pickNumber);
|
|
|
|
|
setTeamTimers((prev) => ({ ...prev, [data.teamId]: data.timeRemaining }));
|
2026-06-06 08:52:04 -07:00
|
|
|
setPickTimerExpiresAt({ teamId: data.teamId, expiresAt: clientExpiresAt(data.timeRemaining) });
|
2026-06-06 05:57:46 +00:00
|
|
|
setIsOvernightPause(false);
|
|
|
|
|
setOvernightResumesAt(null);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleTimerBankUpdated = (data: { teamId: string; timeRemaining: number }) => {
|
|
|
|
|
setTeamTimers((prev) => ({ ...prev, [data.teamId]: data.timeRemaining }));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleTimerOvernightPaused = (data: {
|
|
|
|
|
seasonId: string;
|
|
|
|
|
teamId: string;
|
2026-04-26 22:31:52 -07:00
|
|
|
resumesAtUTC?: number;
|
|
|
|
|
}) => {
|
2026-06-06 05:57:46 +00:00
|
|
|
setPickTimerExpiresAt(null);
|
|
|
|
|
setIsOvernightPause(true);
|
|
|
|
|
setOvernightResumesAt(data.resumesAtUTC ? new Date(data.resumesAtUTC) : null);
|
2026-04-23 22:09:25 -07:00
|
|
|
};
|
|
|
|
|
|
2026-06-06 08:52:04 -07:00
|
|
|
const handleDraftPaused = (data: { seasonId: string; paused: boolean; timers?: Array<{ teamId: string; timeRemaining: number }> }) => {
|
2026-06-06 05:57:46 +00:00
|
|
|
setIsPaused(true);
|
|
|
|
|
setPickTimerExpiresAt(null);
|
2026-06-06 08:52:04 -07:00
|
|
|
if (data.timers?.length) {
|
|
|
|
|
setTeamTimers((prev) => {
|
|
|
|
|
const updated = { ...prev };
|
|
|
|
|
data.timers?.forEach((t) => { updated[t.teamId] = t.timeRemaining; });
|
|
|
|
|
return updated;
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-06-06 05:57:46 +00:00
|
|
|
};
|
2026-04-23 22:09:25 -07:00
|
|
|
const handleDraftResumed = () => setIsPaused(false);
|
|
|
|
|
const handleDraftCompleted = () => setIsDraftComplete(true);
|
|
|
|
|
|
|
|
|
|
const handleAutodraftUpdated = (data: {
|
|
|
|
|
teamId: string;
|
|
|
|
|
isEnabled: boolean;
|
|
|
|
|
mode: "next_pick" | "while_on";
|
|
|
|
|
queueOnly: boolean;
|
|
|
|
|
source?: "commissioner" | "user";
|
2026-05-23 18:29:13 -07:00
|
|
|
reason?: "queue_empty" | "pick_complete";
|
2026-04-23 22:09:25 -07:00
|
|
|
}) => {
|
|
|
|
|
setAutodraftStatus((prev) => ({
|
|
|
|
|
...prev,
|
|
|
|
|
[data.teamId]: { isEnabled: data.isEnabled, mode: data.mode, queueOnly: data.queueOnly },
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
if (userTeam && data.teamId === userTeam.id) {
|
|
|
|
|
if (data.source === "commissioner") {
|
|
|
|
|
if (!data.isEnabled) {
|
|
|
|
|
toast.info("Commissioner turned off your autodraft");
|
|
|
|
|
} else {
|
|
|
|
|
toast.info(`Commissioner changed your autodraft to "${getAutodraftLabel(data.isEnabled, data.mode, data.queueOnly)}"`);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2026-05-23 18:29:13 -07:00
|
|
|
if (!data.isEnabled && data.reason === "pick_complete") {
|
|
|
|
|
toast.info("Autodraft turned off — next pick complete");
|
|
|
|
|
} else if (!data.isEnabled && data.reason === "queue_empty") {
|
2026-04-23 22:09:25 -07:00
|
|
|
toast.info("Autodraft disabled — your queue is empty");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setUserAutodraft({ isEnabled: data.isEnabled, mode: data.mode, queueOnly: data.queueOnly });
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleTeamConnected = (data: { teamId: string }) => {
|
|
|
|
|
setConnectedTeams((prev) => new Set(prev).add(data.teamId));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleTeamDisconnected = (data: { teamId: string }) => {
|
|
|
|
|
setConnectedTeams((prev) => {
|
|
|
|
|
const newSet = new Set(prev);
|
|
|
|
|
newSet.delete(data.teamId);
|
|
|
|
|
return newSet;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleConnectedTeamsList = (data: { teamIds: string[] }) => {
|
|
|
|
|
setConnectedTeams(() => new Set(data.teamIds));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleParticipantRemovedFromQueues = (data: { participantId: string }) => {
|
|
|
|
|
setQueue((prev) => prev.filter((item) => item.participantId !== data.participantId));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleQueueEligibilityPruned = (data: { teamId: string; removedParticipantIds: string[] }) => {
|
|
|
|
|
if (data.teamId !== userTeam?.id) return;
|
|
|
|
|
const removed = new Set(data.removedParticipantIds);
|
|
|
|
|
setQueue((prev) => prev.filter((item) => !removed.has(item.participantId)));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleQueueUpdated = (data: { queue: QueueItem[] }) => {
|
|
|
|
|
if (pendingQueueMutationsRef.current > 0) return;
|
|
|
|
|
setQueue(() => data.queue);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handlePickReplaced = (data: { pickNumber: number; pick: DraftPick }) => {
|
|
|
|
|
setPicks((prev) => prev.map((p) => (p.pickNumber === data.pickNumber ? data.pick : p)));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDraftRolledBack = (data: { pickNumber: number }) => {
|
|
|
|
|
setPicks((prev) => prev.filter((p) => p.pickNumber < data.pickNumber));
|
|
|
|
|
setCurrentPick(data.pickNumber);
|
|
|
|
|
setIsDraftComplete(false);
|
2026-06-06 05:57:46 +00:00
|
|
|
setIsPaused(true);
|
|
|
|
|
setPickTimerExpiresAt(null);
|
2026-04-23 22:09:25 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDraftStateSync = (data: {
|
|
|
|
|
picks: DraftPicks;
|
|
|
|
|
currentPickNumber: number;
|
|
|
|
|
isPaused: boolean;
|
|
|
|
|
status: string;
|
2026-06-06 05:57:46 +00:00
|
|
|
isOvernightPause?: boolean;
|
|
|
|
|
overnightResumesAt?: number;
|
|
|
|
|
timers?: Array<{ teamId: string; timeRemaining: number; expiresAt?: number }>;
|
2026-04-23 22:09:25 -07:00
|
|
|
queue?: QueueItem[];
|
2026-04-29 11:49:26 -07:00
|
|
|
watchlistParticipantIds?: string[];
|
2026-04-23 22:09:25 -07:00
|
|
|
}) => {
|
|
|
|
|
if (!isRevalidatingRef.current) {
|
|
|
|
|
setPicks(() => data.picks);
|
|
|
|
|
setCurrentPick(data.currentPickNumber);
|
|
|
|
|
setIsPaused(data.isPaused);
|
|
|
|
|
setIsDraftComplete(data.status === "active" || data.status === "completed");
|
|
|
|
|
if (data.timers) {
|
|
|
|
|
setTeamTimers((prev) => {
|
|
|
|
|
const updated = { ...prev };
|
|
|
|
|
data.timers?.forEach((t) => { updated[t.teamId] = t.timeRemaining; });
|
|
|
|
|
return updated;
|
|
|
|
|
});
|
2026-06-06 05:57:46 +00:00
|
|
|
// Restore countdown for the active team. Filter out already-expired timestamps
|
|
|
|
|
// so a reconnect after autopick fired doesn't briefly show a stale countdown.
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
const activeTimer = data.timers.find((t) => t.expiresAt !== undefined && t.expiresAt > now);
|
2026-06-06 07:30:45 +00:00
|
|
|
if (activeTimer) {
|
2026-06-06 08:52:04 -07:00
|
|
|
setPickTimerExpiresAt({ teamId: activeTimer.teamId, expiresAt: clientExpiresAt(activeTimer.timeRemaining) });
|
2026-06-06 05:57:46 +00:00
|
|
|
} else {
|
|
|
|
|
setPickTimerExpiresAt(null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Restore overnight-pause state for clients that connect mid-pause.
|
|
|
|
|
if (data.isOvernightPause) {
|
|
|
|
|
setIsOvernightPause(true);
|
|
|
|
|
setOvernightResumesAt(data.overnightResumesAt ? new Date(data.overnightResumesAt) : null);
|
|
|
|
|
setPickTimerExpiresAt(null);
|
|
|
|
|
} else {
|
|
|
|
|
setIsOvernightPause(false);
|
|
|
|
|
setOvernightResumesAt(null);
|
2026-04-23 22:09:25 -07:00
|
|
|
}
|
|
|
|
|
if (data.queue) {
|
|
|
|
|
const q = data.queue;
|
|
|
|
|
setQueue(() => q);
|
|
|
|
|
}
|
2026-04-29 11:49:26 -07:00
|
|
|
if (data.watchlistParticipantIds) {
|
|
|
|
|
setWatchedParticipantIds(new Set(data.watchlistParticipantIds));
|
|
|
|
|
}
|
2026-04-23 22:09:25 -07:00
|
|
|
}
|
2026-04-30 20:33:00 -07:00
|
|
|
setIsSyncing(false);
|
2026-04-23 22:09:25 -07:00
|
|
|
};
|
|
|
|
|
|
2026-04-29 11:49:26 -07:00
|
|
|
const handleWatchlistUpdated = (data: { participantIds: string[] }) => {
|
|
|
|
|
setWatchedParticipantIds(new Set(data.participantIds));
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-30 10:14:14 -07:00
|
|
|
const handleDraftRoomClosed = () => {
|
|
|
|
|
setRoomClosed(true);
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-04 20:31:44 -07:00
|
|
|
const handleBracktEvsUpdated = (data: {
|
|
|
|
|
updates: Array<{ participantId: string; vorpValue: number }>;
|
|
|
|
|
}) => {
|
|
|
|
|
setBracktVorps((prev) => {
|
|
|
|
|
const next = new Map(prev);
|
|
|
|
|
for (const { participantId, vorpValue } of data.updates) {
|
|
|
|
|
next.set(participantId, vorpValue);
|
|
|
|
|
}
|
|
|
|
|
return next;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-23 22:09:25 -07:00
|
|
|
on("pick-made", handlePickMade as (data: unknown) => void);
|
2026-06-06 05:57:46 +00:00
|
|
|
on("timer-bank-updated", handleTimerBankUpdated as (data: unknown) => void);
|
|
|
|
|
on("timer-pick-started", handleTimerPickStarted as (data: unknown) => void);
|
|
|
|
|
on("timer-overnight-paused", handleTimerOvernightPaused as (data: unknown) => void);
|
2026-04-23 22:09:25 -07:00
|
|
|
on("draft-paused", handleDraftPaused as (data: unknown) => void);
|
|
|
|
|
on("draft-resumed", handleDraftResumed as (data: unknown) => void);
|
|
|
|
|
on("draft-completed", handleDraftCompleted as (data: unknown) => void);
|
|
|
|
|
on("autodraft-updated", handleAutodraftUpdated as (data: unknown) => void);
|
|
|
|
|
on("team-connected", handleTeamConnected as (data: unknown) => void);
|
|
|
|
|
on("team-disconnected", handleTeamDisconnected as (data: unknown) => void);
|
|
|
|
|
on("connected-teams-list", handleConnectedTeamsList as (data: unknown) => void);
|
|
|
|
|
on("participant-removed-from-queues", handleParticipantRemovedFromQueues as (data: unknown) => void);
|
|
|
|
|
on("queue-eligibility-pruned", handleQueueEligibilityPruned as (data: unknown) => void);
|
|
|
|
|
on("queue-updated", handleQueueUpdated as (data: unknown) => void);
|
|
|
|
|
on("pick-replaced", handlePickReplaced as (data: unknown) => void);
|
|
|
|
|
on("draft-rolled-back", handleDraftRolledBack as (data: unknown) => void);
|
|
|
|
|
on("draft-state-sync", handleDraftStateSync as (data: unknown) => void);
|
2026-04-29 11:49:26 -07:00
|
|
|
on("watchlist-updated", handleWatchlistUpdated as (data: unknown) => void);
|
2026-04-30 10:14:14 -07:00
|
|
|
on("draft-room-closed", handleDraftRoomClosed as (data: unknown) => void);
|
2026-05-04 20:31:44 -07:00
|
|
|
on("brackt-evs-updated", handleBracktEvsUpdated as (data: unknown) => void);
|
2026-04-23 22:09:25 -07:00
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
off("pick-made", handlePickMade as (data: unknown) => void);
|
2026-06-06 05:57:46 +00:00
|
|
|
off("timer-bank-updated", handleTimerBankUpdated as (data: unknown) => void);
|
|
|
|
|
off("timer-pick-started", handleTimerPickStarted as (data: unknown) => void);
|
|
|
|
|
off("timer-overnight-paused", handleTimerOvernightPaused as (data: unknown) => void);
|
2026-04-23 22:09:25 -07:00
|
|
|
off("draft-paused", handleDraftPaused as (data: unknown) => void);
|
|
|
|
|
off("draft-resumed", handleDraftResumed as (data: unknown) => void);
|
|
|
|
|
off("draft-completed", handleDraftCompleted as (data: unknown) => void);
|
|
|
|
|
off("autodraft-updated", handleAutodraftUpdated as (data: unknown) => void);
|
|
|
|
|
off("team-connected", handleTeamConnected as (data: unknown) => void);
|
|
|
|
|
off("team-disconnected", handleTeamDisconnected as (data: unknown) => void);
|
|
|
|
|
off("connected-teams-list", handleConnectedTeamsList as (data: unknown) => void);
|
|
|
|
|
off("participant-removed-from-queues", handleParticipantRemovedFromQueues as (data: unknown) => void);
|
|
|
|
|
off("queue-eligibility-pruned", handleQueueEligibilityPruned as (data: unknown) => void);
|
|
|
|
|
off("queue-updated", handleQueueUpdated as (data: unknown) => void);
|
|
|
|
|
off("pick-replaced", handlePickReplaced as (data: unknown) => void);
|
|
|
|
|
off("draft-rolled-back", handleDraftRolledBack as (data: unknown) => void);
|
|
|
|
|
off("draft-state-sync", handleDraftStateSync as (data: unknown) => void);
|
2026-04-29 11:49:26 -07:00
|
|
|
off("watchlist-updated", handleWatchlistUpdated as (data: unknown) => void);
|
2026-04-30 10:14:14 -07:00
|
|
|
off("draft-room-closed", handleDraftRoomClosed as (data: unknown) => void);
|
2026-05-04 20:31:44 -07:00
|
|
|
off("brackt-evs-updated", handleBracktEvsUpdated as (data: unknown) => void);
|
2026-04-23 22:09:25 -07:00
|
|
|
};
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
|
}, [on, off, socketVersion]);
|
|
|
|
|
}
|