Merge pull request 'Fix draft pause clock: use Math.ceil consistently via shared msToSeconds helper' (#78) from fix/draft-pause-clock-rounding into main
Reviewed-on: #78
This commit is contained in:
commit
6ba8adab5b
4 changed files with 13 additions and 6 deletions
|
|
@ -113,6 +113,11 @@ export function getInitialDraftSpeed(season: DraftSpeedSource): string {
|
||||||
return "standard";
|
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.
|
* Anchors a server-provided remaining-seconds value to the local client clock.
|
||||||
* Use this everywhere we convert timeRemaining → an expiresAt timestamp so the
|
* 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 { NotificationSettings } from "~/components/NotificationSettings";
|
||||||
import { AutodraftBadgeWithPopover, AutodraftSettings } from "~/components/AutodraftSettings";
|
import { AutodraftBadgeWithPopover, AutodraftSettings } from "~/components/AutodraftSettings";
|
||||||
import { toast } from "sonner";
|
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 { Users, LayoutGrid, ListChecks, Settings, ListOrdered } from "lucide-react";
|
||||||
import type { Route } from "./+types/$leagueId.draft.$seasonId";
|
import type { Route } from "./+types/$leagueId.draft.$seasonId";
|
||||||
|
|
||||||
|
|
@ -509,7 +509,7 @@ export default function DraftRoom() {
|
||||||
// without skew, then re-anchor to client clock.
|
// without skew, then re-anchor to client clock.
|
||||||
const active = timers.find((t) => t.picksExpiresAt && t.picksExpiresAt.getTime() > loaderTimerNow);
|
const active = timers.find((t) => t.picksExpiresAt && t.picksExpiresAt.getTime() > loaderTimerNow);
|
||||||
if (!active?.picksExpiresAt) return null;
|
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) };
|
return { teamId: active.teamId, expiresAt: clientExpiresAt(secondsRemaining) };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -517,7 +517,7 @@ export default function DraftRoom() {
|
||||||
if (!pickTimerExpiresAt) return;
|
if (!pickTimerExpiresAt) return;
|
||||||
const { teamId, expiresAt } = pickTimerExpiresAt;
|
const { teamId, expiresAt } = pickTimerExpiresAt;
|
||||||
const tick = () => {
|
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 });
|
setTeamTimers((prev) => prev[teamId] === remaining ? prev : { ...prev, [teamId]: remaining });
|
||||||
};
|
};
|
||||||
tick(); // immediate update so display is accurate on mount
|
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 { calculatePickInfo } from "~/models/draft-utils";
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
import { db } from "./db";
|
import { db } from "./db";
|
||||||
|
import { msToSeconds } from "~/lib/draft-timer";
|
||||||
|
|
||||||
// Socket event types
|
// Socket event types
|
||||||
interface ServerToClientEvents {
|
interface ServerToClientEvents {
|
||||||
|
|
@ -327,7 +328,7 @@ export function initializeSocketIO(httpServer: HTTPServer): SocketIOServer {
|
||||||
const expiresAt = t.picksExpiresAt?.getTime();
|
const expiresAt = t.picksExpiresAt?.getTime();
|
||||||
const computedRemaining =
|
const computedRemaining =
|
||||||
expiresAt && expiresAt > nowMs
|
expiresAt && expiresAt > nowMs
|
||||||
? Math.max(0, Math.floor((expiresAt - nowMs) / 1000))
|
? msToSeconds(expiresAt - nowMs)
|
||||||
: t.timeRemaining;
|
: t.timeRemaining;
|
||||||
return {
|
return {
|
||||||
teamId: t.teamId,
|
teamId: t.teamId,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { checkOvernightPause, evictOvernightPauseCache, overnightPauseCacheKeys,
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
import { db } from "./db";
|
import { db } from "./db";
|
||||||
import { startDraft } from "~/services/draft-autostart";
|
import { startDraft } from "~/services/draft-autostart";
|
||||||
|
import { msToSeconds } from "~/lib/draft-timer";
|
||||||
|
|
||||||
// Per-season in-memory state
|
// Per-season in-memory state
|
||||||
const pickTimeouts = new Map<string, NodeJS.Timeout>(); // seasonId → active setTimeout
|
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 msUntilExpiry = expiresAt.getTime() - now;
|
||||||
const timeRemaining = Math.max(0, Math.ceil(msUntilExpiry / 1000));
|
const timeRemaining = msToSeconds(msUntilExpiry);
|
||||||
|
|
||||||
// Tell clients to start their local countdown.
|
// Tell clients to start their local countdown.
|
||||||
try {
|
try {
|
||||||
|
|
@ -427,7 +428,7 @@ export async function onDraftPaused(seasonId: string): Promise<Array<{ teamId: s
|
||||||
activeTimers
|
activeTimers
|
||||||
.filter((t) => t.picksExpiresAt !== null)
|
.filter((t) => t.picksExpiresAt !== null)
|
||||||
.map(async (timer) => {
|
.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
|
await db
|
||||||
.update(schema.draftTimers)
|
.update(schema.draftTimers)
|
||||||
.set({ timeRemaining: remaining, picksExpiresAt: null, picksStartedAt: null, updatedAt: new Date() })
|
.set({ timeRemaining: remaining, picksExpiresAt: null, picksStartedAt: null, updatedAt: new Date() })
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue