SHA256
Доработать финал регистрации для мобильного UI
This commit is contained in:
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
client.version=1.2.326
|
client.version=1.2.327
|
||||||
server.version=1.2.297
|
server.version=1.2.298
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user