chore(tracker): убрать отладку — console.log, debug-бейдж, server-лог
Прогресс работает, отладочная обвязка больше не нужна: - tracker.js: удалены все console.log/console.warn (boot, click, POST, HTTP-ответ, patch-успех), удалены ensureDebugBadge и updateDebugBadge (визуальный бейдж в правом нижнем углу), recordParaVisit больше не вызывает updateDebugBadge - 5 хуков (bubble, capture, setParaTab-patch, .tab[refN] sidebar, polling .active) сохранены в production-виде — без логов, но с теми же действиями - backend/routes/textbooks.js: убран '[progress]' console.log из POST /:slug/progress Pre-commit hook теперь проходит без --no-verify.
This commit is contained in:
@@ -214,8 +214,6 @@ router.get('/:slug', (req, res) => {
|
||||
|
||||
/* POST /api/textbooks/:slug/progress — update progress */
|
||||
router.post('/:slug/progress', (req, res) => {
|
||||
// DEBUG: log incoming progress writes
|
||||
console.log('[progress]', new Date().toISOString(), 'user', req.user && req.user.id, '| slug=', req.params.slug, '| body=', JSON.stringify(req.body || {}));
|
||||
const t = db.prepare('SELECT id FROM textbooks WHERE slug=? AND is_active=1').get(req.params.slug);
|
||||
if (!t) return res.status(404).json({ error: 'Учебник не найден' });
|
||||
|
||||
|
||||
@@ -31,32 +31,26 @@
|
||||
let syncPending = false;
|
||||
let pendingExtra = null;
|
||||
function syncToServer(extra) {
|
||||
if (typeof LS === 'undefined' || !LS.getToken) { console.warn('[tracker] LS не загружен — пропускаем sync'); return; }
|
||||
if (!LS.getToken()) { console.warn('[tracker] нет токена в localStorage — пользователь не залогинен'); return; }
|
||||
if (typeof LS === 'undefined' || !LS.getToken || !LS.getToken()) return;
|
||||
if (syncPending) {
|
||||
pendingExtra = Object.assign(pendingExtra || {}, extra || {});
|
||||
return;
|
||||
}
|
||||
syncPending = true;
|
||||
const body = JSON.stringify({ last_para: localState.last, ...(extra || {}) });
|
||||
console.log('[tracker]', slug, '→ POST', body);
|
||||
fetch('/api/textbooks/' + slug + '/progress', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + LS.getToken(),
|
||||
},
|
||||
body,
|
||||
}).then(r => {
|
||||
console.log('[tracker]', slug, '← HTTP', r.status);
|
||||
if (!r.ok) r.text().then(t => console.warn('[tracker] ошибка:', t));
|
||||
body: JSON.stringify({ last_para: localState.last, ...(extra || {}) }),
|
||||
}).finally(() => {
|
||||
syncPending = false;
|
||||
if (pendingExtra) {
|
||||
const next = pendingExtra; pendingExtra = null;
|
||||
syncToServer(next);
|
||||
}
|
||||
}).catch(e => console.warn('[tracker] fetch упал:', e));
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
/* ── 2. Initial load: merge server data into local state + push back ──
|
||||
@@ -235,100 +229,63 @@
|
||||
refreshCheckUI(key);
|
||||
persist();
|
||||
syncToServer({ mark_read: key });
|
||||
updateDebugBadge();
|
||||
}
|
||||
|
||||
// DEBUG: визуальный бейдж прогресса в правом нижнем углу.
|
||||
function ensureDebugBadge() {
|
||||
if (document.getElementById('tb-debug-badge')) return;
|
||||
const b = document.createElement('div');
|
||||
b.id = 'tb-debug-badge';
|
||||
b.style.cssText = 'position:fixed;bottom:12px;right:12px;z-index:99999;background:rgba(15,23,42,.92);color:#fff;padding:10px 14px;border-radius:10px;font-family:monospace;font-size:12px;line-height:1.5;box-shadow:0 4px 14px rgba(0,0,0,.3);max-width:260px';
|
||||
document.body.appendChild(b);
|
||||
updateDebugBadge();
|
||||
}
|
||||
function updateDebugBadge() {
|
||||
const b = document.getElementById('tb-debug-badge');
|
||||
if (!b) return;
|
||||
const activeParaEl = document.querySelector('.para-pill.active[data-para]');
|
||||
const active = activeParaEl ? activeParaEl.dataset.para : '?';
|
||||
b.innerHTML =
|
||||
'<div><b>tracker debug</b></div>' +
|
||||
'<div>slug: ' + slug + '</div>' +
|
||||
'<div>active pill: ' + active + '</div>' +
|
||||
'<div>localState.last: ' + (localState.last || '?') + '</div>' +
|
||||
'<div>read [' + localState.read.length + ']: ' + (localState.read.slice(-8).join(',') || '—') + '</div>' +
|
||||
'<div style="opacity:.6;margin-top:4px">Кликай пилюли — должно меняться</div>';
|
||||
}
|
||||
|
||||
function wirePillTracking() {
|
||||
// Hook 1: всплытие click до body. Работает в обычном HTML.
|
||||
// Hook 1: всплытие click → body. Работает в обычном HTML.
|
||||
document.body.addEventListener('click', e => {
|
||||
const pill = e.target && e.target.closest && e.target.closest('.para-pill[data-para]');
|
||||
if (!pill) return;
|
||||
console.log('[tracker] клик по пилюле (bubble)', pill.dataset.para);
|
||||
recordParaVisit(pill.dataset.para);
|
||||
});
|
||||
// Hook 2: capture-фаза (ловит до того, как кто-то остановит propagation).
|
||||
// Hook 2: capture-фаза — ловит до того, как кто-то остановит propagation.
|
||||
document.addEventListener('click', e => {
|
||||
const pill = e.target && e.target.closest && e.target.closest('.para-pill[data-para]');
|
||||
if (!pill) return;
|
||||
// Защита от двойного срабатывания — отметим pill сразу.
|
||||
if (pill.__tbVisited) return;
|
||||
pill.__tbVisited = true;
|
||||
setTimeout(() => { pill.__tbVisited = false; }, 100);
|
||||
console.log('[tracker] клик по пилюле (capture)', pill.dataset.para);
|
||||
recordParaVisit(pill.dataset.para);
|
||||
}, true);
|
||||
// Hook 3: monkey-patch setParaTab — кликом по пилюле химия/физика 9 вызывают
|
||||
// inline onclick="setParaTab('pN')". Перехват напрямую = работает даже если
|
||||
// event-bubbling сломан расширением браузера или CSS overlay.
|
||||
// Hook 3: monkey-patch setParaTab. chemistry-9 / physics-9 зовут её inline
|
||||
// через onclick="setParaTab('pN')" — перехват на уровне JS-функции
|
||||
// работает даже если event-стек сломан расширением или overlay.
|
||||
function patchSetParaTab() {
|
||||
if (typeof window.setParaTab !== 'function' || window.setParaTab.__tbPatched) return;
|
||||
const orig = window.setParaTab;
|
||||
const wrapped = function (para) {
|
||||
try {
|
||||
if (para && /^p\d+$/i.test(String(para))) {
|
||||
console.log('[tracker] setParaTab перехвачен', para);
|
||||
recordParaVisit(String(para));
|
||||
}
|
||||
} catch (e) { console.warn('[tracker] patch error:', e); }
|
||||
if (para && /^p\d+$/i.test(String(para))) recordParaVisit(String(para));
|
||||
} catch (e) {}
|
||||
return orig.apply(this, arguments);
|
||||
};
|
||||
wrapped.__tbPatched = true;
|
||||
window.setParaTab = wrapped;
|
||||
console.log('[tracker] setParaTab успешно обёрнут');
|
||||
}
|
||||
patchSetParaTab();
|
||||
// Если страница определяет setParaTab позже — поймаем через короткие опросы.
|
||||
let tries = 0;
|
||||
const ivl = setInterval(() => {
|
||||
patchSetParaTab();
|
||||
if (typeof window.setParaTab === 'function' && window.setParaTab.__tbPatched) clearInterval(ivl);
|
||||
if (++tries > 20) clearInterval(ivl);
|
||||
}, 100);
|
||||
// Hook 4b: боковая панель-справочник в chemistry-9 / physics-9 использует
|
||||
// .tab[data-tab="refN"] вместо .para-pill. Маппим ref<N> → p<N> и фиксируем.
|
||||
// Hook 4: справочник в chemistry-9 / physics-9 использует .tab[data-tab=refN]
|
||||
// (отдельная панель). Маппим ref<N> → p<N> и фиксируем как просмотр параграфа.
|
||||
document.addEventListener('click', e => {
|
||||
const tab = e.target && e.target.closest && e.target.closest('.tab[data-tab]');
|
||||
if (!tab) return;
|
||||
const m = String(tab.dataset.tab || '').match(/^ref(\d+)$/);
|
||||
if (!m) return;
|
||||
const para = 'p' + m[1];
|
||||
console.log('[tracker] клик по справ. табу', tab.dataset.tab, '→', para);
|
||||
recordParaVisit(para);
|
||||
recordParaVisit('p' + m[1]);
|
||||
}, true);
|
||||
// Hook 4: polling — наблюдаем за классом .active на пилюлях.
|
||||
// Если кто-то поменял активный параграф (через клик, через JS вызов
|
||||
// setParaTab, через любой механизм) — мы это поймаем за 500мс и зафиксируем.
|
||||
// Самый robust способ; не зависит ни от событий, ни от наличия функций.
|
||||
// Hook 5: polling — наблюдаем за классом .active на пилюлях.
|
||||
// Срабатывает на любую программную смену активного параграфа.
|
||||
let lastActivePara = null;
|
||||
setInterval(() => {
|
||||
const active = document.querySelector('.para-pill.active[data-para]');
|
||||
if (!active) return;
|
||||
const para = active.dataset.para;
|
||||
if (para && para !== lastActivePara) {
|
||||
if (lastActivePara !== null) console.log('[tracker] активный параграф изменился на', para);
|
||||
lastActivePara = para;
|
||||
recordParaVisit(para);
|
||||
}
|
||||
@@ -566,9 +523,6 @@
|
||||
}
|
||||
|
||||
function boot() {
|
||||
console.log('[tracker] boot, slug =', slug, '| LS:', typeof LS !== 'undefined', '| token:', typeof LS !== 'undefined' && LS.getToken && !!LS.getToken());
|
||||
ensureDebugBadge();
|
||||
setInterval(updateDebugBadge, 1000);
|
||||
injectStyles();
|
||||
installBackButton();
|
||||
installBookmarksBtn();
|
||||
|
||||
Reference in New Issue
Block a user