Emit standings-updated socket event after recalculate-floors so league pages refresh automatically
When the admin runs "Recalculate Floors" on the bracket page, the league sports-season homepage was showing stale elimination data because there was no mechanism to notify it of the change. Fix: after recalculate-floors updates participant_results, emit a standings-updated socket event to all fantasy-season draft rooms linked to the sports season. The sports-season page now joins its draft room and revalidates its loader whenever it receives that event for the matching sports season. https://claude.ai/code/session_01D3NbnVQdaH82Fk7Jm8y3sB
This commit is contained in:
parent
f356bdce03
commit
90a4039cba
3 changed files with 37 additions and 1 deletions
|
|
@ -52,6 +52,7 @@ import { logger } from "~/lib/logger";
|
||||||
import { database } from "~/database/context";
|
import { database } from "~/database/context";
|
||||||
import * as schema from "~/database/schema";
|
import * as schema from "~/database/schema";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
import { getSocketIO } from "../../../server/socket";
|
||||||
|
|
||||||
export async function loader({ params }: Route.LoaderArgs) {
|
export async function loader({ params }: Route.LoaderArgs) {
|
||||||
const sportsSeason = await findSportsSeasonById(params.id);
|
const sportsSeason = await findSportsSeasonById(params.id);
|
||||||
|
|
@ -622,6 +623,19 @@ export async function action({ request, params }: Route.ActionArgs) {
|
||||||
// skipDiscord: recalculate-floors is a data-correction tool, not a result announcement.
|
// skipDiscord: recalculate-floors is a data-correction tool, not a result announcement.
|
||||||
await recalculateAffectedLeagues(event.sportsSeasonId, undefined, { skipDiscord: true });
|
await recalculateAffectedLeagues(event.sportsSeasonId, undefined, { skipDiscord: true });
|
||||||
|
|
||||||
|
// Notify connected league pages to refresh their elimination/standings data.
|
||||||
|
try {
|
||||||
|
const seasonSports = await db.query.seasonSports.findMany({
|
||||||
|
where: eq(schema.seasonSports.sportsSeasonId, event.sportsSeasonId),
|
||||||
|
});
|
||||||
|
const io = getSocketIO();
|
||||||
|
for (const ss of seasonSports) {
|
||||||
|
io.to(`draft-${ss.seasonId}`).emit("standings-updated", { sportsSeasonId: event.sportsSeasonId });
|
||||||
|
}
|
||||||
|
} catch (socketError) {
|
||||||
|
logger.warn("[recalculate-floors] Could not emit standings-updated:", socketError);
|
||||||
|
}
|
||||||
|
|
||||||
return { success: `Recalculated floors from ${sortedMatches.length} completed match(es)` };
|
return { success: `Recalculated floors from ${sortedMatches.length} completed match(es)` };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Error recalculating floors:", error);
|
logger.error("Error recalculating floors:", error);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import { Link } from "react-router";
|
import { Link, useRevalidator } from "react-router";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { io } from "socket.io-client";
|
||||||
import type { Route } from "./+types/$leagueId.sports-seasons.$sportsSeasonId";
|
import type { Route } from "./+types/$leagueId.sports-seasons.$sportsSeasonId";
|
||||||
|
|
||||||
import { loader } from "./$leagueId.sports-seasons.$sportsSeasonId.server";
|
import { loader } from "./$leagueId.sports-seasons.$sportsSeasonId.server";
|
||||||
|
|
@ -69,6 +71,25 @@ export default function SportSeasonDetail({
|
||||||
groupStandings,
|
groupStandings,
|
||||||
} = loaderData;
|
} = loaderData;
|
||||||
|
|
||||||
|
const { revalidate } = useRevalidator();
|
||||||
|
|
||||||
|
// Revalidate when the admin updates standings (e.g. recalculate-floors).
|
||||||
|
useEffect(() => {
|
||||||
|
const socket = io({ path: "/socket.io", transports: ["websocket", "polling"] });
|
||||||
|
socket.on("connect", () => {
|
||||||
|
socket.emit("join-draft", season.id);
|
||||||
|
});
|
||||||
|
socket.on("standings-updated", (data: { sportsSeasonId: string }) => {
|
||||||
|
if (data.sportsSeasonId === sportsSeason.id) {
|
||||||
|
revalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
socket.emit("leave-draft", season.id);
|
||||||
|
socket.disconnect();
|
||||||
|
};
|
||||||
|
}, [season.id, sportsSeason.id, revalidate]);
|
||||||
|
|
||||||
const hasBracket = playoffMatches && playoffMatches.length > 0;
|
const hasBracket = playoffMatches && playoffMatches.length > 0;
|
||||||
const hasStandings = regularSeasonStandings && regularSeasonStandings.length > 0;
|
const hasStandings = regularSeasonStandings && regularSeasonStandings.length > 0;
|
||||||
const showOtLosses = hasStandings && regularSeasonStandings.some((s) => s.otLosses !== null);
|
const showOtLosses = hasStandings && regularSeasonStandings.some((s) => s.otLosses !== null);
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ interface ServerToClientEvents {
|
||||||
"draft-resumed": (data: { seasonId: string; paused: boolean }) => void;
|
"draft-resumed": (data: { seasonId: string; paused: boolean }) => void;
|
||||||
"participant-removed-from-queues": (data: { participantId: string }) => void;
|
"participant-removed-from-queues": (data: { participantId: string }) => void;
|
||||||
"queue-updated": (data: { queue: Array<{ id: string; teamId: string; seasonId: string; participantId: string; queuePosition: number }> }) => void;
|
"queue-updated": (data: { queue: Array<{ id: string; teamId: string; seasonId: string; participantId: string; queuePosition: number }> }) => void;
|
||||||
|
"standings-updated": (data: { sportsSeasonId: string }) => void;
|
||||||
"draft-state-sync": (data: {
|
"draft-state-sync": (data: {
|
||||||
currentPickNumber: number;
|
currentPickNumber: number;
|
||||||
isPaused: boolean;
|
isPaused: boolean;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue