Compare commits

...

1 Commits

Author SHA1 Message Date
root
963ecdd89b fix: auto-next transition deadlock and resume-at-end bug
All checks were successful
Release / Test backend (push) Successful in 47s
Release / Check ui (push) Successful in 1m38s
Release / Docker / caddy (push) Successful in 42s
Release / Docker / backend (push) Successful in 2m38s
Release / Docker / runner (push) Successful in 2m38s
Release / Upload source maps (push) Successful in 1m23s
Release / Docker / ui (push) Successful in 2m26s
Release / Gitea Release (push) Successful in 31s
Bug 1 — Auto-next not transitioning:
audioExpanded defaulted to false on the new chapter page because
audioStore.chapter still held the old chapter number when the page script
initialized. The $effect only opened the panel when isPlaying was already
true — a circular dependency (can't play without the panel, panel only opens
when playing). Fix: also set audioExpanded=true when autoStartChapter targets
this chapter, both in the initial $state and in the reactive $effect.

Bug 2 — Resume starts at the end:
onended called saveAudioTime() which captured currentTime≈duration and fired a
PATCH 2 seconds later (after navigation had already completed). Next visit to
that chapter restored the end-of-file position. Fix: in onended, cancel the
debounced timer (clearTimeout) and immediately PATCH audioTime=0 for the
finished chapter, so it always resumes from the beginning on re-visit.
2026-04-06 21:51:47 +05:00
2 changed files with 24 additions and 5 deletions

View File

@@ -363,7 +363,20 @@
}}
onended={() => {
audioStore.isPlaying = false;
saveAudioTime();
// Cancel any pending debounced save and reset the position to 0 for
// the chapter that just finished. Without this, the 2s debounce fires
// after navigation and saves currentTime≈duration, causing resume to
// start at the very end next time the user returns to this chapter.
clearTimeout(audioTimeSaveTimer);
if (audioStore.slug && audioStore.chapter) {
const slug = audioStore.slug;
const chapter = audioStore.chapter;
fetch('/api/progress/audio-time', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ slug, chapter, audioTime: 0 })
}).catch(() => {});
}
// If sleep-after-chapter is set, just pause instead of navigating
if (audioStore.sleepAfterChapter) {
audioStore.sleepAfterChapter = false;

View File

@@ -311,14 +311,20 @@
return t || `Chapter ${data.chapter.number}`;
});
// Audio panel: auto-open if this chapter is already loaded/playing in the store
// Audio panel: auto-open if this chapter is already loaded/playing in the store,
// OR if auto-next is about to start it (autoStartChapter is set before navigation).
// svelte-ignore state_referenced_locally
let audioExpanded = $state(
audioStore.slug === data.book.slug && audioStore.chapter === data.chapter.number
(audioStore.slug === data.book.slug && audioStore.chapter === data.chapter.number) ||
audioStore.autoStartChapter === data.chapter.number
);
$effect(() => {
// Expand automatically when the store starts playing this chapter
if (audioStore.slug === data.book.slug && audioStore.chapter === data.chapter.number && audioStore.isPlaying) {
// Expand automatically when the store starts playing this chapter,
// or when auto-next targets this chapter (before startPlayback has run).
if (
(audioStore.slug === data.book.slug && audioStore.chapter === data.chapter.number && audioStore.isPlaying) ||
audioStore.autoStartChapter === data.chapter.number
) {
audioExpanded = true;
}
});