fix(home): restart the hero banner timer on a manual change
🏗️ Build and Test JellyTau / Run Tests (push) Failing after 14m18s
🏗️ Build and Test JellyTau / Android Compile Check (push) Skipped
Publish Documentation / Build & publish docs to gitea-pages (push) Successful in 5m31s
Traceability Validation / Check Requirement Traces (push) Successful in 17s
Build & Release / Run Tests (push) Failing after 14m7s
Build & Release / Build Linux (push) Skipped
Build & Release / Build Windows (push) Skipped
Build & Release / Build Android (push) Skipped
Build & Release / Create Release (push) Skipped

The rotation interval was installed once when the banner mounted and never
touched again, so a swipe, arrow or dot tap inherited whatever was left of the
running countdown — swiping 5.5s into a 6s interval moved the banner on half a
second later.

The timer moves into heroRotation.ts as a small restartable object so it can be
unit-tested, and every manual navigation path restarts it from that moment.
Verified red-first: with restart() reverted to leave a running timer alone, the
regression test fails.

Release 0.9.1.
This commit is contained in:
2026-08-20 23:11:30 +02:00
parent 98b2ede8bd
commit 16658889a2
11 changed files with 168 additions and 12 deletions
+40
View File
@@ -0,0 +1,40 @@
// Auto-rotation timer for the home-screen hero banner.
//
// Extracted from HeroBanner.svelte so it can be unit-tested: a manual swipe or
// dot/arrow click must restart the countdown from that moment. The old code
// installed one bare setInterval and left it running, so swiping late in an
// interval made the banner jump to the next item almost immediately.
//
// TRACES: UR-034 | DR-038 | UT-207
export interface RotationTimer {
/** (Re)start the countdown from now, replacing any pending tick. */
restart(): void;
/** Cancel the countdown. */
stop(): void;
isRunning(): boolean;
}
/**
* Create a repeating timer that calls `onElapse` every `interval` ms once
* started. Restarting is idempotent — there is never more than one live timer.
*/
export function createRotationTimer(interval: number, onElapse: () => void): RotationTimer {
let handle: ReturnType<typeof setInterval> | null = null;
function stop() {
if (handle !== null) {
clearInterval(handle);
handle = null;
}
}
return {
restart() {
stop();
handle = setInterval(onElapse, interval);
},
stop,
isRunning: () => handle !== null,
};
}