Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ed37f78c7 | ||
|
|
963ecdd89b |
@@ -248,6 +248,12 @@
|
||||
audioStore.nextChapter = nextChapter ?? null;
|
||||
});
|
||||
|
||||
// Keep chapters list in store up to date so the layout's onended announce
|
||||
// can find titles even if startPlayback() hasn't been called yet on this mount.
|
||||
$effect(() => {
|
||||
if (chapters.length > 0) audioStore.chapters = chapters;
|
||||
});
|
||||
|
||||
// Keep voices in store up to date whenever prop changes.
|
||||
$effect(() => {
|
||||
if (voices.length > 0) audioStore.voices = voices;
|
||||
|
||||
@@ -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;
|
||||
@@ -393,8 +406,25 @@
|
||||
const text = `Chapter ${targetChapter}${titlePart}`;
|
||||
window.speechSynthesis.cancel();
|
||||
const utterance = new SpeechSynthesisUtterance(text);
|
||||
utterance.onend = doNavigate;
|
||||
utterance.onerror = doNavigate;
|
||||
|
||||
// Guard: ensure doNavigate can only fire once even if both
|
||||
// onend and the timeout fire, or onerror fires after onend.
|
||||
let navigated = false;
|
||||
const safeNavigate = () => {
|
||||
if (navigated) return;
|
||||
navigated = true;
|
||||
clearTimeout(announceTimeout);
|
||||
doNavigate();
|
||||
};
|
||||
|
||||
// Hard fallback: if speechSynthesis silently drops the utterance
|
||||
// (common on Chrome Android due to gesture policy, or when the
|
||||
// browser is busy fetching the next chapter's audio), navigate
|
||||
// anyway after a generous 8-second window.
|
||||
const announceTimeout = setTimeout(safeNavigate, 8000);
|
||||
|
||||
utterance.onend = safeNavigate;
|
||||
utterance.onerror = safeNavigate;
|
||||
window.speechSynthesis.speak(utterance);
|
||||
} else {
|
||||
doNavigate();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user