Fix reader read-aloud races and add rows.Err checks
- reader.js: guard read-aloud against the loading placeholder (bodyReady) and invalidate stale speechSynthesis callbacks with a generation token - storage: check rows.Err() after iterating story-view/content-length reads - metrics: reuse placeholders() instead of duplicating the IN-clause builder
This commit is contained in:
@@ -44,6 +44,7 @@
|
||||
var open = false;
|
||||
var contentCache = {}; // id -> {content, lede}
|
||||
var inflight = null;
|
||||
var bodyReady = false; // true once the real article body is rendered (not the loading placeholder)
|
||||
|
||||
// ---- read-state store -----------------------------------------------------
|
||||
function loadRead() {
|
||||
@@ -264,6 +265,7 @@
|
||||
}
|
||||
|
||||
function loadingBody(it) {
|
||||
bodyReady = false; // read-aloud waits for the real body, not this placeholder
|
||||
renderBody(it, '<p style="opacity:.55">Loading the full story…</p>');
|
||||
}
|
||||
|
||||
@@ -274,6 +276,7 @@
|
||||
html = '<p style="opacity:.7">No article text was captured for this story. Open the original to read it.</p>';
|
||||
}
|
||||
renderBody(it, html);
|
||||
bodyReady = true;
|
||||
}
|
||||
|
||||
function fetchContent(it) {
|
||||
@@ -483,6 +486,8 @@
|
||||
|
||||
// ---- read aloud (signed-in only) ------------------------------------------
|
||||
var speaking = false;
|
||||
var speakGen = 0; // bumped on every stop/start; stale utterance callbacks that
|
||||
// fire after speechSynthesis.cancel() check it and no-op
|
||||
var speakParas = []; // <p> elements being read, for highlight
|
||||
var speakQueue = []; // remaining {el, text} to utter
|
||||
function speakingText() {
|
||||
@@ -502,6 +507,7 @@
|
||||
}
|
||||
function stopSpeak() {
|
||||
if (!TTS_OK) return;
|
||||
speakGen++;
|
||||
try { window.speechSynthesis.cancel(); } catch (e) {}
|
||||
speaking = false;
|
||||
speakQueue = [];
|
||||
@@ -518,15 +524,18 @@
|
||||
speakParas.push(item.el);
|
||||
try { item.el.scrollIntoView({ block: "center", behavior: "smooth" }); } catch (e) {}
|
||||
}
|
||||
var gen = speakGen;
|
||||
var u = new SpeechSynthesisUtterance(item.text);
|
||||
u.onend = function () { if (speaking) speakNext(); };
|
||||
u.onerror = function () { if (speaking) speakNext(); };
|
||||
u.onend = function () { if (speaking && gen === speakGen) speakNext(); };
|
||||
u.onerror = function () { if (speaking && gen === speakGen) speakNext(); };
|
||||
try { window.speechSynthesis.speak(u); } catch (e) { stopSpeak(); }
|
||||
}
|
||||
function startSpeak() {
|
||||
if (!TTS_OK) return;
|
||||
if (!bodyReady) { toast("Still loading…"); return; }
|
||||
var parts = speakingText();
|
||||
if (!parts.length) { toast("Nothing to read yet"); return; }
|
||||
speakGen++;
|
||||
try { window.speechSynthesis.cancel(); } catch (e) {}
|
||||
speaking = true;
|
||||
speakQueue = parts.slice();
|
||||
|
||||
Reference in New Issue
Block a user