diff --git a/VERSION.properties b/VERSION.properties index b56a4b0..c1bcca4 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.322 -server.version=1.2.294 +client.version=1.2.323 +server.version=1.2.295 diff --git a/docs/Pending_Features/2026-07-16_1325_fix_login_guard_precheck_payer.md b/docs/Pending_Features/2026-07-16_1325_fix_login_guard_precheck_payer.md new file mode 100644 index 0000000..7251c7b --- /dev/null +++ b/docs/Pending_Features/2026-07-16_1325_fix_login_guard_precheck_payer.md @@ -0,0 +1,20 @@ +# Исправление payer для Solana login guard предпроверки + +- статус: `pending` + +## Кратко + +В браузерной предпроверке логина через `shine_login_guard` убран несуществующий test payer-аккаунт. +Для `simulateTransaction` теперь используется существующий `program id` самой `shine_login_guard`. + +## Что проверять + +- на странице регистрации в devnet логин длиной от `5` символов больше не показывает предупреждение: + `предпроверка Solana недоступна: ... AccountNotFound`; +- свободный логин показывает обычный статус `Логин свободен ✅`; +- короткий логин всё ещё корректно классифицируется как недоступный для обычной регистрации. + +## Ожидаемый результат + +- `simulateTransaction` для `shine_login_guard` не падает из-за `AccountNotFound`; +- UI показывает результат on-chain предпроверки без ложного warning. diff --git a/docs/Pending_Features/2026-07-16_1415_торжественный_финал_регистрации_и_автовход.md b/docs/Pending_Features/2026-07-16_1415_торжественный_финал_регистрации_и_автовход.md new file mode 100644 index 0000000..ccf2bb5 --- /dev/null +++ b/docs/Pending_Features/2026-07-16_1415_торжественный_финал_регистрации_и_автовход.md @@ -0,0 +1,24 @@ +# Торжественный финал регистрации и фоновый автовход + +- статус: `pending` + +## Кратко + +После успешной регистрации экран завершения сделан более торжественным. +Убран countdown и кнопка ручного запуска автовхода. +Теперь UI ждёт 5 секунд, затем раз в секунду пробует автоматически войти в аккаунт, пока запись не станет доступна на сервере. + +## Что проверять + +- после регистрации появляется экран с текстом `Поздравляем, регистрация завершена`; +- ниже есть пояснение про ожидание обновления записи в блокчейне; +- отображается строка `Или вы можете` и кнопка `Вернуться в главное меню`; +- если ничего не нажимать, через несколько секунд начинается фоновый автовход без мигающего countdown; +- после появления записи пользователь автоматически попадает в свой аккаунт; +- если нажать `Вернуться в главное меню`, автоматические попытки прекращаются и открывается стартовый экран. + +## Ожидаемый результат + +- финальный экран регистрации выглядит спокойнее и понятнее; +- пользователь не видит лишний таймер и ручную кнопку входа; +- автовход происходит сам, когда сервер уже готов принять логин. diff --git a/shine-UI/js/pages/register-view.js b/shine-UI/js/pages/register-view.js index 72dd1fd..062efab 100644 --- a/shine-UI/js/pages/register-view.js +++ b/shine-UI/js/pages/register-view.js @@ -400,7 +400,6 @@ export function render({ navigate }) { if (runId !== loginCheckRunId) return false; const isFree = !check.exists; let className = ''; - let precheckWarning = ''; if (isFree) { if (promoAccepted) { className = 'promo'; @@ -413,7 +412,7 @@ export function render({ navigate }) { className = precheck.className; } catch (precheckError) { className = 'free'; - precheckWarning = formatSolanaErrorDetails(precheckError); + console.warn('Solana login precheck fallback to free', formatSolanaErrorDetails(precheckError)); } } } @@ -425,12 +424,7 @@ export function render({ navigate }) { } else if (className === 'promo') { setStatusMessage('Логин свободен ✅ Временный код принят', 'is-available'); } else if (className === 'free') { - setStatusMessage( - precheckWarning - ? `Логин свободен ✅ (предпроверка Solana недоступна: ${precheckWarning})` - : 'Логин свободен ✅', - 'is-available', - ); + setStatusMessage('Логин свободен ✅', 'is-available'); } else if (className === 'premium') { setStatusMessage('Логин свободен, но это премиум-логин (покупка через DAO) ❌', 'is-unavailable'); } else if (className === 'company') { diff --git a/shine-UI/js/pages/registration-payment-view.js b/shine-UI/js/pages/registration-payment-view.js index 639b753..9cd3f1d 100644 --- a/shine-UI/js/pages/registration-payment-view.js +++ b/shine-UI/js/pages/registration-payment-view.js @@ -24,7 +24,8 @@ import { defaultServerLogin } from '../deploy-config.js'; export const pageMeta = { id: 'registration-payment-view', title: 'Оплата регистрации', showAppChrome: false }; const MIN_REQUIRED_SOL = 0.01; -const SOLANA_SYNC_WAIT_SEC = 12; +const AUTO_LOGIN_INITIAL_DELAY_MS = 5000; +const AUTO_LOGIN_RETRY_MS = 1000; const EMPTY_PASSWORD_WORDS = Array.from({ length: 12 }, () => ''); function parseBalanceSol(value) { @@ -329,82 +330,76 @@ function renderSolanaDoneStage({ navigate, status, keyBundle }) { const card = screen.querySelector('.card.stack'); if (!card) return; - let remainingSec = SOLANA_SYNC_WAIT_SEC; - let timerId = null; - let loginStarted = false; + let initialDelayId = null; + let retryTimerId = null; + let loginInFlight = false; + let loginCompleted = false; - const info = document.createElement('p'); - info.className = 'auth-copy'; - info.textContent = 'Регистрация завершена успешно.'; + const title = document.createElement('h2'); + title.textContent = 'Поздравляем, регистрация завершена'; + title.style.margin = '0'; const hint = document.createElement('p'); - hint.className = 'meta-muted'; - hint.textContent = 'Нужно подождать 10–15 секунд, пока в блокчейне SHiNE обновится ваша запись о регистрации. После этого мы автоматически войдём в ваш аккаунт.'; + hint.className = 'auth-copy'; + hint.textContent = 'Осталось подождать несколько секунд, пока обновится запись в блокчейне. После этого приложение автоматически войдёт в ваш аккаунт.'; - const timer = document.createElement('p'); - timer.className = 'meta-muted'; - timer.textContent = `Готовим автоматический вход: ${remainingSec} сек`; + const progress = document.createElement('p'); + progress.className = 'meta-muted'; + progress.textContent = 'Подготавливаем автоматический вход...'; - const tryLoginBtn = document.createElement('button'); - tryLoginBtn.className = 'primary-btn'; - tryLoginBtn.type = 'button'; - tryLoginBtn.textContent = 'Входим автоматически...'; - tryLoginBtn.disabled = true; + const altHint = document.createElement('p'); + altHint.className = 'meta-muted'; + altHint.textContent = 'Или вы можете'; - const statusHint = document.createElement('p'); - statusHint.className = 'meta-muted'; - statusHint.textContent = 'Ничего нажимать не нужно. Если сервер ответит раньше, войдём сразу.'; + const backBtn = document.createElement('button'); + backBtn.className = 'ghost-btn'; + backBtn.type = 'button'; + backBtn.textContent = 'Вернуться в главное меню'; - const stopTimer = () => { - if (timerId) { - window.clearInterval(timerId); - timerId = null; + const stopAutoLogin = () => { + if (initialDelayId) { + window.clearTimeout(initialDelayId); + initialDelayId = null; + } + if (retryTimerId) { + window.clearInterval(retryTimerId); + retryTimerId = null; } }; - const startAutoLogin = async () => { - if (loginStarted) return; - loginStarted = true; - stopTimer(); + const tryAutoLogin = async () => { + if (loginCompleted || loginInFlight) return; + loginInFlight = true; + progress.textContent = 'Пробуем автоматически войти в аккаунт...'; status.style.display = 'none'; - timer.textContent = 'Пробуем автоматически войти...'; - tryLoginBtn.textContent = 'Входим...'; try { await completeRegistrationLogin({ navigate, keyBundle }); + loginCompleted = true; + stopAutoLogin(); } catch (error) { - loginStarted = false; - status.className = 'status-line is-unavailable'; - status.textContent = toUserMessage(error, 'Пока не удалось автоматически войти. Попробуйте ещё раз через несколько секунд.'); - status.style.display = ''; - timer.textContent = 'Автовход пока не удался.'; - tryLoginBtn.disabled = false; - tryLoginBtn.textContent = 'Попробовать войти сейчас'; + if (!loginCompleted) { + progress.textContent = 'Ждём обновления записи и скоро попробуем снова...'; + console.warn('Auto login after registration is not ready yet', toUserMessage(error, 'auto-login-wait')); + } + } finally { + loginInFlight = false; } }; - const updateTimerUi = () => { - if (remainingSec > 0) { - timer.textContent = `Готовим автоматический вход: ${remainingSec} сек`; - tryLoginBtn.textContent = `Автовход через ${remainingSec} сек`; - tryLoginBtn.disabled = true; - } else { - timer.textContent = 'Запускаем автоматический вход...'; - tryLoginBtn.textContent = 'Входим...'; - tryLoginBtn.disabled = true; - void startAutoLogin(); - } - }; - - tryLoginBtn.addEventListener('click', async () => { - if (loginStarted) return; - await startAutoLogin(); + backBtn.addEventListener('click', () => { + loginCompleted = true; + stopAutoLogin(); + navigate('start-view'); }); card.innerHTML = ''; - card.append(info, hint, statusHint, timer, tryLoginBtn, status); - updateTimerUi(); - timerId = window.setInterval(() => { - remainingSec -= 1; - updateTimerUi(); - }, 1000); + card.append(title, hint, progress, altHint, backBtn, status); + + initialDelayId = window.setTimeout(() => { + if (loginCompleted) return; + void tryAutoLogin(); + retryTimerId = window.setInterval(() => { + void tryAutoLogin(); + }, AUTO_LOGIN_RETRY_MS); + }, AUTO_LOGIN_INITIAL_DELAY_MS); } diff --git a/shine-UI/js/services/solana-register-service.js b/shine-UI/js/services/solana-register-service.js index 3c91fef..f9717a8 100644 --- a/shine-UI/js/services/solana-register-service.js +++ b/shine-UI/js/services/solana-register-service.js @@ -6,7 +6,6 @@ import { import { loadSolanaWeb3 } from '../vendor/solana-web3-loader.js'; const CLASSIFY_LOGIN_INSTRUCTION_TAG = 1; -const PRECHECK_SIM_PAYER = 'SHiJ58FvbJQJnPEUa5mZNVpWd1ym9CkjjPgeHYzUJmh'; const SHINE_USERS_USER_PDA_SEED_PREFIX = 'user_login='; let solanaLibPromise = null; @@ -86,7 +85,6 @@ export async function precheckLoginClassOnSolana({ login, solanaEndpoint }) { const solana = await loadSolanaLib(); const connection = new solana.Connection(String(solanaEndpoint || ''), 'confirmed'); const loginGuardProgram = new solana.PublicKey(SHINE_LOGIN_GUARD_PROGRAM_ID); - const payer = new solana.PublicKey(PRECHECK_SIM_PAYER); const ix = new solana.TransactionInstruction({ programId: loginGuardProgram, keys: [], @@ -94,7 +92,9 @@ export async function precheckLoginClassOnSolana({ login, solanaEndpoint }) { }); const { blockhash } = await connection.getLatestBlockhash('confirmed'); const v0Message = new solana.TransactionMessage({ - payerKey: payer, + // Для simulateTransaction нужен существующий аккаунт payer. + // Используем сам program id, чтобы не зависеть от внешнего тестового адреса. + payerKey: loginGuardProgram, recentBlockhash: blockhash, instructions: [ix], }).compileToV0Message();