fix live leaderbord details

This commit is contained in:
larssand
2026-08-28 21:12:43 +02:00
parent 3cf68cf34a
commit c6262aceab
15 changed files with 293 additions and 34 deletions

View File

@@ -140,7 +140,7 @@ const checkAppVersion = () => checkAppVersionHelper({ getBackendUrl, getBaseline
const startAppVersionPolling = () => startAppVersionPollingHelper({ checkAppVersion, getAppVersionPollTimer: () => appVersionPollTimer, setAppVersionPollTimer: (value) => { appVersionPollTimer = value; } });
const startOverlaySync = () => startOverlaySyncHelper({ getOverlaySyncTimer: () => overlaySyncTimer, setOverlaySyncTimer: (value) => { overlaySyncTimer = value; }, getLocalStateDirty: () => localStateDirty, hydrateFromBackend, getCurrentView: () => currentView, renderView });
const startOverlayRotation = () => startOverlayRotationHelper({ getOverlayRotationTimer: () => overlayRotationTimer, setOverlayRotationTimer: (value) => { overlayRotationTimer = value; }, getOverlayRotationIndex: () => overlayRotationIndex, setOverlayRotationIndex: (value) => { overlayRotationIndex = value; }, getCurrentView: () => currentView, getOverlayViewMode: () => overlayViewMode, renderView });
const startOverlayLiveRefresh = () => startOverlayLiveRefreshHelper({ getOverlayLiveRefreshTimer: () => overlayLiveRefreshTimer, setOverlayLiveRefreshTimer: (value) => { overlayLiveRefreshTimer = value; }, getCurrentView: () => currentView, getOverlayViewMode: () => overlayViewMode, renderOverlay });
const startOverlayLiveRefresh = () => startOverlayLiveRefreshHelper({ getOverlayLiveRefreshTimer: () => overlayLiveRefreshTimer, setOverlayLiveRefreshTimer: (value) => { overlayLiveRefreshTimer = value; }, getCurrentView: () => currentView, getOverlayViewMode: () => overlayViewMode, refreshOverlayDynamics });
const ensureAudioContext = () => ensureAudioContextHelper({ state, getAudioCtx: () => audioCtx, setAudioCtx: (value) => { audioCtx = value; } });
const playPassingBeep = () => playPassingBeepHelper({ ensureAudioContext });
const playFinishSiren = () => playFinishSirenHelper({ ensureAudioContext });
@@ -180,7 +180,7 @@ const startSessionWithAudio = (session, callbacks) => {
const practice = ["open_practice", "free_practice", "practice"].includes(String(session?.type || ""));
return raceAudioEngine.runStartSequence(session, { ...callbacks, skipAudio: practice });
};
const tickClock = () => tickClockHelper({ dom, currentLanguage, handleSessionTimerTick, getActiveSession, getCurrentView: () => currentView, getOverlayMode: () => overlayMode, renderView, updateHeaderState });
const tickClock = () => tickClockHelper({ dom, currentLanguage, handleSessionTimerTick, getActiveSession, getCurrentView: () => currentView, getOverlayMode: () => overlayMode, renderView, refreshTimingDynamics, updateHeaderState });
const buildTeamRaceResultsHtml = (event) => buildTeamRaceResultsHtmlHelper(event, { t, escapeHtml, resolveEventBranding, getClassName, buildPrintBrandBlock, buildTeamRaceStandings, renderTable, formatLap, renderTeamStintLog, renderSessionChannelAnalysis });
const connectDecoder = () => connectDecoderHelper({
state,
@@ -239,6 +239,48 @@ const renderDrivers = () => renderDriversView({ state, dom, t, driverBrandFilter
const renderCars = () => renderCarsView({ state, dom, t, carBrandFilter: () => carBrandFilter, setCarBrandFilter: (value) => { carBrandFilter = value; }, selectedCarEditId: () => selectedCarEditId, setSelectedCarEditId: (value) => { selectedCarEditId = value; }, uid, saveState, renderView, renderTable, escapeHtml, setFormError, bindModalShell, normalizeCar });
const renderGuide = () => renderGuideView({ dom, t });
const renderOverlay = () => renderOverlayPageView({ state, dom, t, escapeHtml, formatLap, formatCountdown, formatElapsedClock, formatPredictedLapDelta, getActiveSession, buildLeaderboard, ensureSessionResult, getSessionTiming, getVisiblePassings, resolveEventBranding, buildPracticeStandings, buildQualifyingStandings, buildFinalStandings, getOverlayModeLabel, getStatusLabel, getObsOverlayConfig, buildOverlayPanels, normalizeStartMode, renderPositionGrid, getEventName, getSessionTypeLabel, getStartModeLabel, renderRaceStandingsTableView, renderTeamOverlay, renderObsOverlay, renderOverlayLeaderboard, renderOverlaySidePanel, formatTeamActiveMemberLabel, getSessionGridEntries, overlayViewMode, overlayMode, publicOverlayMode, overlayEvents, overlayRotationIndex, openOverlayWindow, buildOverlayUrl });
const refreshOverlayDynamics = () => {
const active = getActiveSession();
if (!active) return;
const timing = getSessionTiming(active);
const clock = timing.followUpActive
? formatCountdown(timing.followUpRemainingMs)
: timing.untimed
? formatElapsedClock(timing.elapsedMs)
: formatCountdown(timing.remainingMs);
document.querySelectorAll("[data-overlay-clock]").forEach((node) => {
node.textContent = clock;
});
const rowsByKey = new Map(buildLeaderboard(active).map((row) => [String(row.key), row]));
document.querySelectorAll("[data-overlay-competitor]").forEach((node) => {
const row = rowsByKey.get(String(node.dataset.overlayCompetitor || ""));
if (!row) return;
const prediction = node.querySelector("[data-overlay-prediction]");
const fill = node.querySelector("[data-overlay-prediction-fill]");
if (prediction) prediction.textContent = formatPredictedLapDelta(row.predictedRemainingMs);
if (fill instanceof HTMLElement) {
fill.style.width = `${Math.max(0, Math.min(100, Math.round(Math.min(1, row.predictedProgress || 0) * 100)))}%`;
fill.classList.remove("overlay-prediction-good", "overlay-prediction-warn", "overlay-prediction-overdue");
fill.classList.add(`overlay-prediction-${row.predictionTone || "good"}`);
}
});
};
const refreshTimingDynamics = () => {
const active = getActiveSession();
if (!active) return;
const timing = getSessionTiming(active);
const clock = timing.followUpActive
? formatCountdown(timing.followUpRemainingMs)
: timing.untimed
? formatElapsedClock(timing.elapsedMs)
: formatCountdown(timing.remainingMs);
document.querySelectorAll("[data-timing-clock]").forEach((node) => {
node.textContent = clock;
});
};
const renderTiming = () => renderTimingView({ state, dom, t, escapeHtml, formatLap, formatCountdown, formatElapsedClock, formatRaceClock, renderTable, uid, getActiveSession, ensureSessionResult, buildLeaderboard, getSessionTiming, getVisiblePassings, getEventName, getSessionTypeLabel, getStatusLabel, normalizeStartMode, getStartModeLabel, renderPositionGrid, renderSessionChannelAnalysis, connectDecoder, disconnectDecoder, applyCompetitorCorrection, invalidateCompetitorLastLap, restoreCompetitorLastInvalidLap, ensureAudioContext, validateTrackSessionForStart, announceRaceStarted, announceRaceFinished, startSessionWithAudio, cancelRaceStart, triggerRaceAudio, getStartLightState: () => raceAudioEngine.getStartLightState(), saveState, updateHeaderState, renderView, normalizeDriver, normalizeCar, processDecoderMessage, getSelectedLeaderboardKey: () => selectedLeaderboardKey, setSelectedLeaderboardKey: (value) => { selectedLeaderboardKey = value; }, getQuickAddDraft: () => quickAddDraft, setQuickAddDraft: (value) => { quickAddDraft = value; }, getPassingValidationLabel, isCountedPassing, getCompetitorPassings, getManualCorrectionSummary, formatTeamActiveMemberLabel, lastFinishAnnouncementSessionId: () => lastFinishAnnouncementSessionId, setLastFinishAnnouncementSessionId: (value) => { lastFinishAnnouncementSessionId = value; }, lastOverlayLeaderKeyBySession, lastOverlayTop3BySession, overlayEvents: () => overlayEvents, setOverlayEvents: (value) => { overlayEvents = value; } });
const renderJudging = () => renderJudgingView({ state, dom, t, escapeHtml, formatLap, renderTable, getActiveSession, ensureSessionResult, buildLeaderboard, getSessionTypeLabel, getJudgeFilteredRows, getJudgeFilteredLog, getCompetitorPassings, isCountedPassing, getPassingValidationLabel, undoJudgingAdjustment, applyCompetitorCorrection, invalidateCompetitorLastLap, restoreCompetitorLastInvalidLap, renderView, getSelectedJudgeKey: () => selectedJudgeKey, setSelectedJudgeKey: (value) => { selectedJudgeKey = value; }, getJudgingCompetitorFilter: () => judgingCompetitorFilter, setJudgingCompetitorFilter: (value) => { judgingCompetitorFilter = value; }, getJudgingLogFilter: () => judgingLogFilter, setJudgingLogFilter: (value) => { judgingLogFilter = value; } });
const normalizeTrainingState = (training) => normalizeTrainingStateLogic(training, { uid });