fix(tracker): mark_read шлётся на КАЖДЫЙ клик пилюли (идемпотентно)

Старый syncPending-баг успел залить локальный localState.read данными,
которых нет на сервере. После фиксов firstTime=false для всех ключей в
localState.read, и mark_read иначе никогда не уходил → каталог показывал
0 даже после реальных кликов.

Решение: убрать оптимизацию firstTime. Слать mark_read КАЖДЫЙ раз —
серверный код  if(!arr.includes(mark_read)) arr.push(...)  не добавит
дубликат. Лишний POST стоит копейки, зато система самовосстанавливается
без зависимости от загрузочного backfill.
This commit is contained in:
Maxim Dolgolyov
2026-05-27 17:17:00 +03:00
parent 89ddc4f68f
commit 25c0bb2a79
+9 -8
View File
@@ -214,23 +214,24 @@
localState.read.forEach(k => { refreshPillUI(k); refreshCheckUI(k); }); localState.read.forEach(k => { refreshPillUI(k); refreshCheckUI(k); });
} }
/* ── 7. Pill click → last_para + (первый раз) mark_read ──────── /* ── 7. Pill click → ВСЕГДА шлём last_para + mark_read одним POST
Объединяем оба обновления в один POST, чтобы syncPending-guard Идемпотентно: на сервере `if (!arr.includes(mark_read)) arr.push(...)`
в syncToServer не дропнул второй вызов в том же тике. */ не добавит дубликат. Эта «избыточность» лечит самовосстановлением
ситуации, когда localState.read был засорён старым syncPending-багом
(есть ключ локально, нет на сервере, mark_read иначе никогда не уйдёт). */
function wirePillTracking() { function wirePillTracking() {
document.body.addEventListener('click', e => { document.body.addEventListener('click', e => {
const pill = e.target.closest('.para-pill[data-para]'); const pill = e.target.closest('.para-pill[data-para]');
if (!pill) return; if (!pill) return;
const key = pill.dataset.para; const key = pill.dataset.para;
localState.last = key; localState.last = key;
const firstTime = !localState.read.includes(key); if (!localState.read.includes(key)) {
if (firstTime) {
localState.read.push(key); localState.read.push(key);
refreshPillUI(key);
refreshCheckUI(key);
} }
refreshPillUI(key);
refreshCheckUI(key);
persist(); persist();
syncToServer(firstTime ? { mark_read: key } : {}); syncToServer({ mark_read: key });
}); });
} }