fix: homepage overlay blocker and carousel auto-advance

- Add pointer-events:none to ListeningMode fly-transition wrapper div in
  +layout.svelte so the exiting animation div never blocks page interaction
- Add pointer-events:auto to ListeningMode root div so it still captures
  all touch/click events correctly despite the parent being pointer-events:none
- Rewrite carousel auto-advance using $effect + autoAdvanceSeed pattern:
  replaces the stale-closure setInterval in resetAutoAdvance() with a
  reactive $effect that owns the interval and re-starts cleanly on manual
  navigation by bumping a seed counter
This commit is contained in:
root
2026-04-06 16:58:23 +05:00
parent 9d925382b3
commit 395bfdb976
3 changed files with 20 additions and 13 deletions

View File

@@ -256,6 +256,7 @@
transition: {isDragging ? 'none' : 'transform 0.32s cubic-bezier(0.32,0.72,0,1), opacity 0.32s ease'}; transition: {isDragging ? 'none' : 'transform 0.32s cubic-bezier(0.32,0.72,0,1), opacity 0.32s ease'};
will-change: transform; will-change: transform;
touch-action: pan-x; touch-action: pan-x;
pointer-events: auto;
" "
ontouchstart={onTouchStart} ontouchstart={onTouchStart}
ontouchend={onTouchEnd} ontouchend={onTouchEnd}

View File

@@ -954,7 +954,7 @@
<!-- Listening mode — mounted at root level, independent of audioStore.active, <!-- Listening mode — mounted at root level, independent of audioStore.active,
so closing/pausing audio never tears it down and loses context. --> so closing/pausing audio never tears it down and loses context. -->
{#if listeningModeOpen} {#if listeningModeOpen}
<div transition:fly={{ y: '100%', duration: 320, opacity: 1 }}> <div transition:fly={{ y: '100%', duration: 320, opacity: 1 }} style="pointer-events: none;">
<ListeningMode <ListeningMode
onclose={() => { listeningModeOpen = false; listeningModeChapters = false; }} onclose={() => { listeningModeOpen = false; listeningModeChapters = false; }}
openChapters={listeningModeChapters} openChapters={listeningModeChapters}

View File

@@ -95,22 +95,28 @@
resetAutoAdvance(); resetAutoAdvance();
} }
let autoAdvanceTimer = $state<ReturnType<typeof setInterval> | null>(null); // Auto-advance carousel every 6 s when there are multiple books.
// We use a $state counter as a "restart token" so the $effect can be
function resetAutoAdvance() { // re-triggered by manual navigation without reading heroIndex (which would
if (autoAdvanceTimer) clearInterval(autoAdvanceTimer); // cause an infinite loop when the interval itself mutates heroIndex).
if (heroBooks.length > 1) { let autoAdvanceSeed = $state(0);
autoAdvanceTimer = setInterval(() => {
heroIndex = (heroIndex + 1) % heroBooks.length;
}, 6000);
}
}
$effect(() => { $effect(() => {
resetAutoAdvance(); if (heroBooks.length <= 1) return;
return () => { if (autoAdvanceTimer) clearInterval(autoAdvanceTimer); }; // Subscribe to heroBooks.length and autoAdvanceSeed only — not heroIndex.
const len = heroBooks.length;
void autoAdvanceSeed; // track the seed
const id = setInterval(() => {
heroIndex = (heroIndex + 1) % len;
}, 6000);
return () => clearInterval(id);
}); });
function resetAutoAdvance() {
// Bump the seed to restart the interval after manual navigation.
autoAdvanceSeed++;
}
function playChapter(slug: string, chapter: number) { function playChapter(slug: string, chapter: number) {
audioStore.autoStartChapter = chapter; audioStore.autoStartChapter = chapter;
goto(`/books/${slug}/chapters/${chapter}`); goto(`/books/${slug}/chapters/${chapter}`);