From 347b4aae6660dd9597259c8dca32eb190e941de1 Mon Sep 17 00:00:00 2001 From: kei Date: Sat, 30 May 2026 19:57:27 +0200 Subject: [PATCH] Autosave active splits --- src/ui/LiveSplit.tsx | 70 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/ui/LiveSplit.tsx b/src/ui/LiveSplit.tsx index eb87b8e4..7bca371f 100644 --- a/src/ui/LiveSplit.tsx +++ b/src/ui/LiveSplit.tsx @@ -204,6 +204,9 @@ export class LiveSplit extends React.Component { private scrollEvent: Option; private rightClickEvent: Option; private resizeEvent: Option; + private autoSaveInterval: Option> = null; + private autoSaveInProgress = false; + private autoSaveAfterCurrent = false; constructor(props: Props) { super(props); @@ -353,6 +356,7 @@ export class LiveSplit extends React.Component { this.isDesktopQuery.addEventListener("change", this.mediaQueryChanged); this.handleAutomaticResize(); + this.updateAutoSaveInterval(); const { serviceWorker } = navigator; if (serviceWorker && serviceWorker.controller) { @@ -368,6 +372,7 @@ export class LiveSplit extends React.Component { } public componentWillUnmount() { + this.stopAutoSaveInterval(); window.removeEventListener( "wheel", expect( @@ -1068,6 +1073,66 @@ export class LiveSplit extends React.Component { } } + private shouldAutoSaveSplits(): boolean { + const phase = this.state.commandSink.currentPhase(); + return ( + phase === TimerPhase.Running || + phase === TimerPhase.Paused || + phase === TimerPhase.Ended + ); + } + + private updateAutoSaveInterval(): void { + if (!this.shouldAutoSaveSplits()) { + this.stopAutoSaveInterval(); + return; + } + + if (this.autoSaveInterval !== null) { + return; + } + + // The serialized timer snapshot contains the in-progress attempt, so + // periodically saving the splits keeps browser restarts from wiping the + // current run's progress. + this.autoSaveInterval = setInterval(() => { + void this.autoSaveSplits(); + }, 3000); + } + + private stopAutoSaveInterval(): void { + if (this.autoSaveInterval === null) { + return; + } + + clearInterval(this.autoSaveInterval); + this.autoSaveInterval = null; + } + + private async autoSaveSplits(): Promise { + if (!this.shouldAutoSaveSplits()) { + this.stopAutoSaveInterval(); + return; + } + + if (this.autoSaveInProgress) { + this.autoSaveAfterCurrent = true; + return; + } + + this.autoSaveInProgress = true; + try { + await this.saveSplits(); + } finally { + this.autoSaveInProgress = false; + } + + if (this.autoSaveAfterCurrent) { + this.autoSaveAfterCurrent = false; + void this.autoSaveSplits(); + } + } + onServerConnectionOpened(serverConnection: LiveSplitServer): void { this.setState({ serverConnection }); } @@ -1135,6 +1200,11 @@ export class LiveSplit extends React.Component { if (this.state.serverConnection != null) { this.state.serverConnection.sendEvent(event); } + + this.updateAutoSaveInterval(); + if (this.shouldAutoSaveSplits()) { + void this.autoSaveSplits(); + } } runChanged(): void {