fix(tracker): backfill — local-only mark_read'ы досылаются на сервер при загрузке

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

Решение: loadServerProgress теперь вычисляет diff между local.read
и server.read; для каждого ключа, которого нет на сервере, дёргает
syncToServer({mark_read: k}). Coalesce в pendingExtra гарантирует,
что все запросы упорядочатся.

Эффект: при следующей загрузке учебника каталог автоматически догоняется.
This commit is contained in:
Maxim Dolgolyov
2026-05-27 17:10:33 +03:00
parent dacc0eb4ac
commit 89ddc4f68f
+12 -2
View File
@@ -53,7 +53,11 @@
}).catch(() => {});
}
/* ── 2. Initial load: merge server data into local state ──────── */
/* ── 2. Initial load: merge server data into local state + push back ──
Если в локальном кэше есть ключи, которых нет на сервере
(последствие старого бага syncPending) — досылаем их через
отдельные mark_read POST'ы. Это лечит «вечный 0/N» у пользователей,
которые до фикса уже накликали кучу пилюль. */
function loadServerProgress() {
if (typeof LS === 'undefined' || !LS.getToken || !LS.getToken()) return;
fetch('/api/textbooks/' + slug, {
@@ -62,11 +66,17 @@
.then(r => r.ok ? r.json() : null)
.then(d => {
if (!d || !d.progress) return;
const merged = Array.from(new Set([...(localState.read || []), ...(d.progress.read || [])]));
const serverRead = new Set(d.progress.read || []);
const localRead = localState.read || [];
const missing = localRead.filter(k => !serverRead.has(k));
// объединяем для UI
const merged = Array.from(new Set([...localRead, ...d.progress.read || []]));
localState.read = merged;
if (!localState.last) localState.last = d.progress.last_para;
localStorage.setItem(lsKey, JSON.stringify(localState));
refreshAllUI();
// догоняем сервер последовательно: syncToServer уже коалесцирует
missing.forEach(k => syncToServer({ mark_read: k }));
})
.catch(() => {});
}