diff --git a/src/antigravity_provider/router/web/static/app.js b/src/antigravity_provider/router/web/static/app.js index 463761f..0644f00 100644 --- a/src/antigravity_provider/router/web/static/app.js +++ b/src/antigravity_provider/router/web/static/app.js @@ -8,6 +8,9 @@ const USE_MOCK_FIXTURE = false; let lastAppliedSeq = -1; +// Какой профиль сейчас открыт в окне аккаунта: нужно, чтобы перерисовывать +// его по свежему снапшоту, а не оставлять с заглушкой. +let _openAccountModalProfile = null; let currentSnapshot = null; let activeView = 'overview'; let pollTimer = null; @@ -226,6 +229,18 @@ function applySnapshot(snapshot) { currentSnapshot = snapshot; updateGlobalHeader(); + // Открытое окно аккаунта рисовалось один раз и на опрос не реагировало. + // Если его открыть до того, как придут живые квоты, оно навсегда + // оставалось с заглушкой («Grok 2h — Н/Д») и со статусом «Не проверялся» + // даже после успешной проверки. Перерисовываем по свежему снапшоту. + if (_openAccountModalProfile) { + try { + openAccountDetailsModal(_openAccountModalProfile, true); + } catch (e) { + console.warn('[Hub] Не удалось обновить окно аккаунта:', e); + } + } + if (isFirstLoad) { const params = new URLSearchParams(window.location.search); const targetView = params.get('view'); @@ -1296,6 +1311,7 @@ async function saveHubServerSettings() { // ── MODALS (Account Details, Model Choice, Routing, Wizard) ── function openAccountDetailsModal(profileId) { + _openAccountModalProfile = profileId; if (!currentSnapshot) return; const profile = (currentSnapshot.all_profiles || {})[profileId]; if (!profile) return; @@ -1939,6 +1955,8 @@ function showModal() { } function closeModal() { + _openAccountModalProfile = null; + stopDeviceAuthPolling(); if (elements.modalBackdrop) elements.modalBackdrop.classList.add('hidden'); } diff --git a/tests/test_account_modal_refresh.py b/tests/test_account_modal_refresh.py new file mode 100644 index 0000000..91858bb --- /dev/null +++ b/tests/test_account_modal_refresh.py @@ -0,0 +1,37 @@ +"""Открытое окно аккаунта обязано обновляться по свежему снапшоту. + +Владелец открыл карточку Grok сразу после запуска — до того, как пришли +живые квоты, — и она навсегда осталась с заглушкой «Grok 2h — Н/Д» и +статусом «Не проверялся», хотя сервер уже отдавал 14% расхода подписки и +проверка подключения прошла успешно. + +Окно рисовалось один раз при открытии и на опрос не реагировало. +""" + +from __future__ import annotations + +import pathlib + +APP_JS = pathlib.Path("src/antigravity_provider/router/web/static/app.js").read_text(encoding="utf-8") + + +def test_open_modal_is_tracked_and_redrawn(): + assert "_openAccountModalProfile" in APP_JS, "открытое окно не отслеживается" + + # Объявление обязано идти ДО использования: let не поднимается, и + # обращение к нему раньше объявления роняет обработчик снапшота. + declaration = APP_JS.index("let _openAccountModalProfile") + first_use = APP_JS.index("if (_openAccountModalProfile)") + assert declaration < first_use, "переменная используется раньше объявления" + + +def test_modal_redraw_happens_on_snapshot_apply(): + """Перерисовка должна быть привязана к применению снапшота.""" + head = APP_JS[: APP_JS.index("if (_openAccountModalProfile)")] + assert "currentSnapshot = snapshot" in head, "перерисовка не там, где применяется снапшот" + + +def test_closing_modal_stops_tracking_and_polling(): + block = APP_JS.split("function closeModal()", 1)[1][:300] + assert "_openAccountModalProfile = null" in block, "после закрытия окно продолжает перерисовываться" + assert "stopDeviceAuthPolling" in block, "опрос кода устройства не останавливается при закрытии"