Доработать финал регистрации для мобильного UI

This commit is contained in:
AidarKC
2026-07-16 19:03:54 +04:00
parent d6bc883520
commit febdbc059e
3 changed files with 97 additions and 29 deletions
+2 -2
View File
@@ -1,2 +1,2 @@
client.version=1.2.326 client.version=1.2.327
server.version=1.2.297 server.version=1.2.298
+62 -27
View File
@@ -24,10 +24,29 @@ import { defaultServerLogin } from '../deploy-config.js';
export const pageMeta = { id: 'registration-payment-view', title: 'Оплата регистрации', showAppChrome: false }; export const pageMeta = { id: 'registration-payment-view', title: 'Оплата регистрации', showAppChrome: false };
const MIN_REQUIRED_SOL = 0.01; const MIN_REQUIRED_SOL = 0.01;
const AUTO_LOGIN_INITIAL_DELAY_MS = 5000; const AUTO_LOGIN_INITIAL_DELAY_MS = 10000;
const AUTO_LOGIN_RETRY_MS = 1000; const AUTO_LOGIN_RETRY_MS = 2000;
const EMPTY_PASSWORD_WORDS = Array.from({ length: 12 }, () => ''); const EMPTY_PASSWORD_WORDS = Array.from({ length: 12 }, () => '');
function getExplorerClusterName(endpoint) {
const source = String(endpoint || '').trim().toLowerCase();
if (!source) return 'mainnet-beta';
if (source.includes('devnet')) return 'devnet';
if (source.includes('testnet')) return 'testnet';
return 'mainnet-beta';
}
function makeSolanaExplorerTxUrl(signature, endpoint) {
const cleanSignature = String(signature || '').trim();
if (!cleanSignature) return '';
const cluster = getExplorerClusterName(endpoint);
const url = new URL(`https://explorer.solana.com/tx/${encodeURIComponent(cleanSignature)}`);
if (cluster !== 'mainnet-beta') {
url.searchParams.set('cluster', cluster);
}
return url.toString();
}
function parseBalanceSol(value) { function parseBalanceSol(value) {
const parsed = Number.parseFloat(String(value || '').replace(',', '.')); const parsed = Number.parseFloat(String(value || '').replace(',', '.'));
return Number.isFinite(parsed) ? parsed : 0; return Number.isFinite(parsed) ? parsed : 0;
@@ -262,14 +281,16 @@ export function render({ navigate }) {
// Регистрация на Solana (смарт контракт) // Регистрация на Solana (смарт контракт)
submitButton.textContent = 'Регистрация в Solana...'; submitButton.textContent = 'Регистрация в Solana...';
let registrationTxId = '';
try { try {
await registerUserOnSolana({ const registrationResult = await registerUserOnSolana({
login: state.registrationDraft.login, login: state.registrationDraft.login,
keyBundle, keyBundle,
solanaEndpoint: state.entrySettings.solanaServer, solanaEndpoint: state.entrySettings.solanaServer,
accessServers: [state.entrySettings.shineServerLogin || defaultServerLogin], accessServers: [state.entrySettings.shineServerLogin || defaultServerLogin],
promoCode: '', promoCode: '',
}); });
registrationTxId = String(registrationResult?.signature || '').trim();
} catch (solanaError) { } catch (solanaError) {
const solanaMsg = formatSolanaErrorDetails(solanaError); const solanaMsg = formatSolanaErrorDetails(solanaError);
// Пользователь уже зарегистрирован в Solana — продолжаем // Пользователь уже зарегистрирован в Solana — продолжаем
@@ -278,7 +299,7 @@ export function render({ navigate }) {
} }
} }
renderSolanaDoneStage({ navigate, status, keyBundle }); renderSolanaDoneStage({ navigate, status, keyBundle, registrationTxId });
} catch (error) { } catch (error) {
const message = toUserMessage(error, 'Не удалось завершить регистрацию.'); const message = toUserMessage(error, 'Не удалось завершить регистрацию.');
setAuthError(message); setAuthError(message);
@@ -324,37 +345,47 @@ export function render({ navigate }) {
return screen; return screen;
} }
function renderSolanaDoneStage({ navigate, status, keyBundle }) { function renderSolanaDoneStage({ navigate, status, keyBundle, registrationTxId = '' }) {
const screen = document.querySelector('section.stack'); const screen = document.querySelector('section.stack');
if (!screen) return; if (!screen) return;
const headerBackButton = screen.querySelector('.page-header .header-left .icon-btn');
const card = screen.querySelector('.card.stack'); const card = screen.querySelector('.card.stack');
if (!card) return; if (!card) return;
card.classList.add('registration-finish-card');
let initialDelayId = null; let initialDelayId = null;
let retryTimerId = null; let retryTimerId = null;
let loginInFlight = false; let loginInFlight = false;
let loginCompleted = false; let loginCompleted = false;
const txExplorerUrl = makeSolanaExplorerTxUrl(registrationTxId, state.entrySettings.solanaServer);
const title = document.createElement('h2'); const title = document.createElement('h2');
title.className = 'registration-finish-title';
title.textContent = 'Поздравляем, регистрация завершена'; title.textContent = 'Поздравляем, регистрация завершена';
title.style.margin = '0';
const hint = document.createElement('p'); const hint = document.createElement('p');
hint.className = 'auth-copy'; hint.className = 'auth-copy registration-finish-text';
hint.textContent = 'Осталось подождать несколько секунд, пока обновится запись в блокчейне. После этого приложение автоматически войдёт в ваш аккаунт.'; hint.textContent = 'Подождите 10 секунд, пока обновится транзакция вашей регистрации в блокчейне Solana. После этого вход в аккаунт произойдёт автоматически.';
const txIdLine = document.createElement('p');
txIdLine.className = 'meta-muted registration-finish-tx';
if (registrationTxId && txExplorerUrl) {
const txLabel = document.createElement('span');
txLabel.textContent = 'Tx ID регистрации: ';
const txLink = document.createElement('a');
txLink.className = 'registration-finish-tx-link';
txLink.href = txExplorerUrl;
txLink.target = '_blank';
txLink.rel = 'noopener noreferrer';
txLink.textContent = registrationTxId;
txIdLine.append(txLabel, txLink);
} else {
txIdLine.textContent = 'Tx ID регистрации: ожидаем подтверждённую подпись';
}
const progress = document.createElement('p'); const progress = document.createElement('p');
progress.className = 'meta-muted'; progress.className = 'meta-muted registration-finish-progress';
progress.textContent = 'Подготавливаем автоматический вход...'; progress.textContent = 'Подготавливаем проверку регистрации...';
const altHint = document.createElement('p');
altHint.className = 'meta-muted';
altHint.textContent = 'Или вы можете';
const backBtn = document.createElement('button');
backBtn.className = 'ghost-btn';
backBtn.type = 'button';
backBtn.textContent = 'Вернуться в главное меню';
const stopAutoLogin = () => { const stopAutoLogin = () => {
if (initialDelayId) { if (initialDelayId) {
@@ -370,7 +401,7 @@ function renderSolanaDoneStage({ navigate, status, keyBundle }) {
const tryAutoLogin = async () => { const tryAutoLogin = async () => {
if (loginCompleted || loginInFlight) return; if (loginCompleted || loginInFlight) return;
loginInFlight = true; loginInFlight = true;
progress.textContent = 'Пробуем автоматически войти в аккаунт...'; progress.textContent = 'Проверяем, прошла ли регистрация...';
status.style.display = 'none'; status.style.display = 'none';
try { try {
await completeRegistrationLogin({ navigate, keyBundle }); await completeRegistrationLogin({ navigate, keyBundle });
@@ -378,7 +409,7 @@ function renderSolanaDoneStage({ navigate, status, keyBundle }) {
stopAutoLogin(); stopAutoLogin();
} catch (error) { } catch (error) {
if (!loginCompleted) { if (!loginCompleted) {
progress.textContent = 'Ждём обновления записи и скоро попробуем снова...'; progress.textContent = 'Пока ещё не прошла, подождите ещё секундочку...';
console.warn('Auto login after registration is not ready yet', toUserMessage(error, 'auto-login-wait')); console.warn('Auto login after registration is not ready yet', toUserMessage(error, 'auto-login-wait'));
} }
} finally { } finally {
@@ -386,14 +417,18 @@ function renderSolanaDoneStage({ navigate, status, keyBundle }) {
} }
}; };
backBtn.addEventListener('click', () => { if (headerBackButton) {
loginCompleted = true; const replacement = headerBackButton.cloneNode(true);
stopAutoLogin(); replacement.addEventListener('click', () => {
navigate('start-view'); loginCompleted = true;
}); stopAutoLogin();
navigate('start-view');
});
headerBackButton.replaceWith(replacement);
}
card.innerHTML = ''; card.innerHTML = '';
card.append(title, hint, progress, altHint, backBtn, status); card.append(title, hint, txIdLine, progress, status);
initialDelayId = window.setTimeout(() => { initialDelayId = window.setTimeout(() => {
if (loginCompleted) return; if (loginCompleted) return;
+33
View File
@@ -1324,6 +1324,39 @@
font-size: 13px; font-size: 13px;
} }
.registration-finish-card {
width: 100%;
max-width: 100%;
overflow: hidden;
}
.registration-finish-title {
margin: 0;
font-size: clamp(1.7rem, 6vw, 2.15rem);
line-height: 1.18;
text-wrap: balance;
}
.registration-finish-text,
.registration-finish-tx,
.registration-finish-progress {
max-width: 100%;
overflow-wrap: anywhere;
word-break: break-word;
}
.registration-finish-tx-link {
color: #9fd8ff;
text-decoration: underline;
overflow-wrap: anywhere;
word-break: break-all;
}
.registration-finish-tx-link:hover,
.registration-finish-tx-link:focus-visible {
color: #c9ebff;
}
.session-status { .session-status {
color: var(--text-muted); color: var(--text-muted);
font-size: 13px; font-size: 13px;