Fix draft pause clock: use Math.ceil consistently via shared msToSeconds helper
- Pause snapshot used Math.floor while the client display used Math.ceil, causing the visible clock to round down by 1s on pause click - draft-state-sync also used Math.floor for reconnecting clients - Extract msToSeconds() to app/lib/draft-timer.ts as the single source of truth for all ms→seconds conversions across server and client Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1804544203
commit
77c0f47552
4 changed files with 13 additions and 6 deletions
|
|
@ -113,6 +113,11 @@ export function getInitialDraftSpeed(season: DraftSpeedSource): string {
|
|||
return "standard";
|
||||
}
|
||||
|
||||
/** Converts a millisecond duration to whole seconds, always rounding up. */
|
||||
export function msToSeconds(ms: number): number {
|
||||
return Math.max(0, Math.ceil(ms / 1000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Anchors a server-provided remaining-seconds value to the local client clock.
|
||||
* Use this everywhere we convert timeRemaining → an expiresAt timestamp so the
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ import { useDraftNotifications } from "~/hooks/useDraftNotifications";
|
|||
import { NotificationSettings } from "~/components/NotificationSettings";
|
||||
import { AutodraftBadgeWithPopover, AutodraftSettings } from "~/components/AutodraftSettings";
|
||||
import { toast } from "sonner";
|
||||
import { clientExpiresAt, formatClockTime, getTimerColorClass } from "~/lib/draft-timer";
|
||||
import { clientExpiresAt, formatClockTime, getTimerColorClass, msToSeconds } from "~/lib/draft-timer";
|
||||
import { Users, LayoutGrid, ListChecks, Settings, ListOrdered } from "lucide-react";
|
||||
import type { Route } from "./+types/$leagueId.draft.$seasonId";
|
||||
|
||||
|
|
@ -509,7 +509,7 @@ export default function DraftRoom() {
|
|||
// without skew, then re-anchor to client clock.
|
||||
const active = timers.find((t) => t.picksExpiresAt && t.picksExpiresAt.getTime() > loaderTimerNow);
|
||||
if (!active?.picksExpiresAt) return null;
|
||||
const secondsRemaining = Math.ceil((active.picksExpiresAt.getTime() - loaderTimerNow) / 1000);
|
||||
const secondsRemaining = msToSeconds(active.picksExpiresAt.getTime() - loaderTimerNow);
|
||||
return { teamId: active.teamId, expiresAt: clientExpiresAt(secondsRemaining) };
|
||||
});
|
||||
|
||||
|
|
@ -517,7 +517,7 @@ export default function DraftRoom() {
|
|||
if (!pickTimerExpiresAt) return;
|
||||
const { teamId, expiresAt } = pickTimerExpiresAt;
|
||||
const tick = () => {
|
||||
const remaining = Math.max(0, Math.ceil((expiresAt - Date.now()) / 1000));
|
||||
const remaining = msToSeconds(expiresAt - Date.now());
|
||||
setTeamTimers((prev) => prev[teamId] === remaining ? prev : { ...prev, [teamId]: remaining });
|
||||
};
|
||||
tick(); // immediate update so display is accurate on mount
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { checkOvernightPause } from "./overnight-pause-check";
|
|||
import { calculatePickInfo } from "~/models/draft-utils";
|
||||
import { logger } from "./logger";
|
||||
import { db } from "./db";
|
||||
import { msToSeconds } from "~/lib/draft-timer";
|
||||
|
||||
// Socket event types
|
||||
interface ServerToClientEvents {
|
||||
|
|
@ -327,7 +328,7 @@ export function initializeSocketIO(httpServer: HTTPServer): SocketIOServer {
|
|||
const expiresAt = t.picksExpiresAt?.getTime();
|
||||
const computedRemaining =
|
||||
expiresAt && expiresAt > nowMs
|
||||
? Math.max(0, Math.floor((expiresAt - nowMs) / 1000))
|
||||
? msToSeconds(expiresAt - nowMs)
|
||||
: t.timeRemaining;
|
||||
return {
|
||||
teamId: t.teamId,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { checkOvernightPause, evictOvernightPauseCache, overnightPauseCacheKeys,
|
|||
import { logger } from "./logger";
|
||||
import { db } from "./db";
|
||||
import { startDraft } from "~/services/draft-autostart";
|
||||
import { msToSeconds } from "~/lib/draft-timer";
|
||||
|
||||
// Per-season in-memory state
|
||||
const pickTimeouts = new Map<string, NodeJS.Timeout>(); // seasonId → active setTimeout
|
||||
|
|
@ -259,7 +260,7 @@ async function _schedulePickForSeason(seasonId: string): Promise<void> {
|
|||
}
|
||||
|
||||
const msUntilExpiry = expiresAt.getTime() - now;
|
||||
const timeRemaining = Math.max(0, Math.ceil(msUntilExpiry / 1000));
|
||||
const timeRemaining = msToSeconds(msUntilExpiry);
|
||||
|
||||
// Tell clients to start their local countdown.
|
||||
try {
|
||||
|
|
@ -427,7 +428,7 @@ export async function onDraftPaused(seasonId: string): Promise<Array<{ teamId: s
|
|||
activeTimers
|
||||
.filter((t) => t.picksExpiresAt !== null)
|
||||
.map(async (timer) => {
|
||||
const remaining = Math.max(0, Math.floor(((timer.picksExpiresAt?.getTime() ?? now) - now) / 1000));
|
||||
const remaining = msToSeconds((timer.picksExpiresAt?.getTime() ?? now) - now);
|
||||
await db
|
||||
.update(schema.draftTimers)
|
||||
.set({ timeRemaining: remaining, picksExpiresAt: null, picksStartedAt: null, updatedAt: new Date() })
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue