new raceday and backup

This commit is contained in:
larssand
2026-09-01 07:07:23 +02:00
parent c2bfa97d30
commit f561dd8feb
21 changed files with 1042 additions and 60 deletions

View File

@@ -263,7 +263,7 @@ export function renderTimingView(deps) {
getActiveSession, ensureSessionResult, buildLeaderboard, getSessionTiming, getVisiblePassings,
getEventName, getSessionTypeLabel, getStatusLabel, normalizeStartMode, getStartModeLabel, renderPositionGrid,
connectDecoder, disconnectDecoder, applyCompetitorCorrection, invalidateCompetitorLastLap, restoreCompetitorLastInvalidLap,
ensureAudioContext, validateTrackSessionForStart, announceRaceStarted, announceRaceFinished, startSessionWithAudio, cancelRaceStart, triggerRaceAudio, getStartLightState, saveState, updateHeaderState, renderView,
startOperationalSession, finishOperationalSession, cancelRaceStart, triggerRaceAudio, getStartLightState, saveState, updateHeaderState, renderView,
normalizeDriver, normalizeCar, processDecoderMessage, getSelectedLeaderboardKey, setSelectedLeaderboardKey,
getQuickAddDraft, setQuickAddDraft, getPassingValidationLabel, isCountedPassing, getCompetitorPassings,
getManualCorrectionSummary, formatTeamActiveMemberLabel, formatRaceClock, renderSessionChannelAnalysis,
@@ -601,15 +601,10 @@ export function renderTimingView(deps) {
renderView();
});
document.getElementById("startSession")?.addEventListener("click", () => {
document.getElementById("startSession")?.addEventListener("click", async () => {
const session = getActiveSession();
const startButton = document.getElementById("startSession");
if (!session || startButton?.disabled) return;
ensureAudioContext();
if (session.mode === "track") {
const trackValidation = validateTrackSessionForStart(session);
if (!trackValidation.ok) { alert(trackValidation.message); return; }
}
if (startButton) startButton.disabled = true;
const updateStartDisplay = (label, lights = 0) => {
if (startButton && label) startButton.textContent = label;
@@ -619,54 +614,21 @@ export function renderTimingView(deps) {
panel.querySelector("strong")?.replaceChildren(document.createTextNode(label));
panel.querySelectorAll("[data-start-light]").forEach((node) => node.classList.toggle("is-on", Number(node.dataset.startLight) <= lights));
};
let committed = false;
const commitStart = () => {
if (committed) return;
committed = true;
session.status = "running";
session.startedAt = Date.now();
session.endedAt = null;
session.finishedByTimer = false;
session.followUpStartedAt = null;
session.pausedAt = null;
session.totalPausedMs = 0;
session.followUpPausedMs = 0;
session.audioAnnouncements = [];
setLastFinishAnnouncementSessionId(null);
lastOverlayLeaderKeyBySession[session.id] = null;
lastOverlayTop3BySession[session.id] = [];
setOverlayEvents([]);
ensureSessionResult(session.id);
announceRaceStarted(session);
saveState();
updateHeaderState();
renderView();
};
startSessionWithAudio(session, {
const result = await startOperationalSession(session, {
onCountdown: (seconds) => updateStartDisplay(String(seconds), 0),
onLight: (light) => updateStartDisplay("LIGHT_" + light, light),
onArmed: () => updateStartDisplay("WAIT_RANDOM", 5),
onTone: commitStart,
}).then((started) => {
if (started === false && startButton) { startButton.disabled = false; updateStartDisplay(t("timing.start"), 0); }
}).catch(() => commitStart());
});
if (!result.ok) {
if (result.message) alert(result.message);
if (startButton) startButton.disabled = false;
updateStartDisplay(t("timing.start"), 0);
}
});
document.getElementById("stopSession")?.addEventListener("click", () => {
cancelRaceStart();
const session = getActiveSession();
if (!session) {
return;
}
session.status = "finished";
session.endedAt = Date.now();
session.finishedByTimer = false;
session.followUpStartedAt = null;
setLastFinishAnnouncementSessionId(session.id);
announceRaceFinished();
saveState();
updateHeaderState();
renderView();
if (session) finishOperationalSession(session);
});
document.getElementById("resetSession")?.addEventListener("click", () => {