From 0f79bd4e7029d4795772922ad6c3230ae8000026 Mon Sep 17 00:00:00 2001 From: Rustam Khalimov Date: Fri, 11 Sep 2026 17:52:05 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D1=82=D1=8C=20=D0=BC?= =?UTF-8?q?=D1=91=D1=80=D1=82=D0=B2=D1=8B=D0=B5=20SSE-=D0=B2=D0=B5=D1=82?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=B8=20=D0=B4=D0=B2=D0=BE=D0=B9=D0=BD=D1=83?= =?UTF-8?q?=D1=8E=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B7=D0=B0=D0=B3=D1=80=D1=83?= =?UTF-8?q?=D0=B7=D0=BA=D1=83=20=D0=B4=D0=BE=D1=81=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Удалены ветки boards_changed/pipeline_stats (core их не публикует); перезагрузка при переклассификации коалесцируется scheduleBoardReload. Закрывает TD-SSE-DEAD и TD-DBL-CLICK. --- backlog.md | 4 ++-- src/frontend/src/store/cards.js | 15 ++++++++++++++- src/frontend/src/store/lifecycle.js | 13 ++++--------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/backlog.md b/backlog.md index ddfe9eb..342b64c 100644 --- a/backlog.md +++ b/backlog.md @@ -53,8 +53,8 @@ |---|---|---|---|---| | TD-SETTINGS-UI | Вынос оставшихся вкладок `SettingsView` в отдельные компоненты (частично сделано) | ревью 2026-09-08 | P3 | TECHDEBT | | TD-VIRT | Полная виртуализация длинных колонок (сейчас — прогрессивный рендер «Показать ещё») | ревью, этап 12 | P3 | TECHDEBT | -| TD-SSE-DEAD | Мёртвые SSE-ветки фронта `boards_changed`/`pipeline_stats` (core их не публикует) | этап 12, E | P3 | TECHDEBT | -| TD-DBL-CLICK | Двойная перезагрузка доски у инициатора batch-reclassify (ответ + SSE) | этап 12, E | P3 | TECHDEBT | +| TD-SSE-DEAD | **Сделано (2026-09-11):** мёртвые SSE-ветки фронта `boards_changed`/`pipeline_stats` удалены из `store/lifecycle.js` (core их не публикует) | этап 12, E | P3 | DONE | +| TD-DBL-CLICK | **Сделано (2026-09-11):** перезагрузка доски при batch-переклассификации коалесцируется `scheduleBoardReload()` (ответ + SSE → один запрос) | этап 12, E | P3 | DONE | | TD-TEST-HARNESS | Историческая гонка `FreeTcpPort` — устранена; следить за новыми хост-хелперами | этап 12, E | P3 | TECHDEBT | | TD-OLD-DOCS | Исторические доки несут старые термины под пометками (переписывать не нужно) | docs sweep | P3 | TECHDEBT | | TD-SOURCE-PROVIDER | Провайдеры содержимого источников. **Сделано (2026-09-11):** `TelegramSourceContentProvider` + `ReadSource` RPC + `GET /api/cards/{id}/source` + UI «Обновить из источника». Осталось: провайдеры прочих источников по мере появления | generic source 2026-09-11 | P2 | TECHDEBT | diff --git a/src/frontend/src/store/cards.js b/src/frontend/src/store/cards.js index 3fd0d27..937ad64 100644 --- a/src/frontend/src/store/cards.js +++ b/src/frontend/src/store/cards.js @@ -630,6 +630,18 @@ export async function reloadBoardData() { } } +let _boardReloadTimer = null + +// Коалесцирует частые перезагрузки доски (ответ + SSE) в один прогон — +// убирает двойную перезагрузку у инициатора batch-переклассификации. +export function scheduleBoardReload(delayMs = 250) { + if (_boardReloadTimer) return + _boardReloadTimer = setTimeout(() => { + _boardReloadTimer = null + reloadBoardData() + }, delayMs) +} + // принять ИИ-предложение: колонка становится обычной export async function acceptSuggestedBoard(id) { const b = containerById(id) @@ -669,7 +681,8 @@ export async function reclassifyInbox() { const r = await api.post('/api/cards/reclassify') if (r?.started) { toast(t('cards.reclassifying', { count: r.attempted }), { icon: 'sparkles' }) - await reloadBoardData() + // Перезагрузку доски коалесцирует scheduleBoardReload: SSE cards_reclassified не дублирует запрос. + scheduleBoardReload() } else if (r?.busy) { toast(t('cards.pereklassifikaciya-uzhe-zapushchena'), { icon: 'sparkles' }) } else if (r?.reason) { diff --git a/src/frontend/src/store/lifecycle.js b/src/frontend/src/store/lifecycle.js index e47714f..8c2be24 100644 --- a/src/frontend/src/store/lifecycle.js +++ b/src/frontend/src/store/lifecycle.js @@ -10,8 +10,8 @@ import { applyMlStatus, refreshMlStatus } from './ml.js' import { applyContainers, applyColState, - reloadBoardData, reloadCards, + scheduleBoardReload, upsertCard, flashCard, revealCard, @@ -148,15 +148,10 @@ function startRealtime() { state.dueReminderId = data.id } else if (type === 'system_status') { refreshTgStatus() - } else if (type === 'pipeline_stats' && data) { - // воркер обработал часть очереди: обновляем счётчики вкладки «Обработка» - state.pQueueCounts = { new: data.new || 0, ai: data.ai || 0, total: data.total || 0 } - state.pRejectedCount = data.rejected || 0 - } else if (type === 'boards_changed') { - reloadBoardData() } else if (type === 'cards_reclassified') { - // переклассификация карточки/«Неразобранного» завершилась: мягко перечитываем доску и статус ML - reloadBoardData() + // Переклассификация карточки/«Неразобранного» завершилась: мягко перечитываем доску и статус ML + // (коалесцируется с перезагрузкой инициатора — без двойного запроса). + scheduleBoardReload() refreshMlStatus() } })