SHA256
Навести порядок в deploy и документации проекта
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
} from '../services/solana-wallet-service.js';
|
||||
import { loadSolanaWeb3 } from '../vendor/solana-web3-loader.js';
|
||||
import {
|
||||
checkLoginExistsOnSolana,
|
||||
formatSolanaErrorDetails,
|
||||
isUserAlreadyExistsSolanaError,
|
||||
registerUserOnSolana,
|
||||
@@ -24,8 +25,13 @@ import { defaultServerLogin } from '../deploy-config.js';
|
||||
|
||||
export const pageMeta = { id: 'registration-payment-view', title: 'Оплата регистрации', showAppChrome: false };
|
||||
const MIN_REQUIRED_SOL = 0.01;
|
||||
const AUTO_LOGIN_INITIAL_DELAY_MS = 10000;
|
||||
const AUTO_LOGIN_INITIAL_DELAY_MS = 1000;
|
||||
const AUTO_LOGIN_RETRY_MS = 2000;
|
||||
const REGISTRATION_PROGRESS_DURATION_MS = 12000;
|
||||
const REGISTRATION_POLL_START_DELAY_MS = 4000;
|
||||
const REGISTRATION_POLL_INTERVAL_MS = 2000;
|
||||
const REGISTRATION_CONFIRM_TIMEOUT_MS = 25000;
|
||||
const REGISTRATION_SUCCESS_SETTLE_DELAY_MS = 2000;
|
||||
const EMPTY_PASSWORD_WORDS = Array.from({ length: 12 }, () => '');
|
||||
|
||||
function getExplorerClusterName(endpoint) {
|
||||
@@ -60,6 +66,10 @@ function getCryptoRuntimeState() {
|
||||
return { hasCrypto, hasGetRandomValues, hasSubtle, secureContext };
|
||||
}
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
|
||||
async function completeRegistrationLogin({ navigate, keyBundle }) {
|
||||
await authService.reconnect(state.entrySettings.shineServer);
|
||||
const result = await authService.createSessionForExistingUser(
|
||||
@@ -299,7 +309,7 @@ export function render({ navigate }) {
|
||||
}
|
||||
}
|
||||
|
||||
renderSolanaDoneStage({ navigate, status, keyBundle, registrationTxId });
|
||||
renderSolanaRegistrationStage({ navigate, status, keyBundle, registrationTxId });
|
||||
} catch (error) {
|
||||
const message = toUserMessage(error, 'Не удалось завершить регистрацию.');
|
||||
setAuthError(message);
|
||||
@@ -345,6 +355,192 @@ export function render({ navigate }) {
|
||||
return screen;
|
||||
}
|
||||
|
||||
function renderSolanaRegistrationStage({ navigate, status, keyBundle, registrationTxId = '' }) {
|
||||
const screen = document.querySelector('section.stack');
|
||||
if (!screen) return;
|
||||
const headerBackButton = screen.querySelector('.page-header .header-left .icon-btn');
|
||||
const card = screen.querySelector('.card.stack');
|
||||
if (!card) return;
|
||||
card.classList.add('registration-finish-card');
|
||||
|
||||
let progressTimerId = null;
|
||||
let pollStartTimerId = null;
|
||||
let pollTimerId = null;
|
||||
let successTimerId = null;
|
||||
let confirmationInFlight = false;
|
||||
let stageClosed = false;
|
||||
let successPending = false;
|
||||
let timeoutShown = false;
|
||||
const stageStartedAt = Date.now();
|
||||
const txExplorerUrl = makeSolanaExplorerTxUrl(registrationTxId, state.entrySettings.solanaServer);
|
||||
|
||||
const title = document.createElement('h2');
|
||||
title.className = 'registration-finish-title';
|
||||
title.textContent = 'Идёт регистрация в блокчейне...';
|
||||
|
||||
const hint = document.createElement('p');
|
||||
hint.className = 'auth-copy registration-finish-text';
|
||||
hint.textContent = 'Ждём, пока транзакция будет полностью одобрена сетью 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 progressWrap = document.createElement('div');
|
||||
progressWrap.className = 'registration-progress';
|
||||
|
||||
const progressBar = document.createElement('div');
|
||||
progressBar.className = 'registration-progress-bar';
|
||||
progressWrap.append(progressBar);
|
||||
|
||||
const progress = document.createElement('p');
|
||||
progress.className = 'meta-muted registration-finish-progress';
|
||||
progress.textContent = 'Подготавливаем проверку подтверждения...';
|
||||
|
||||
const pollStatus = document.createElement('p');
|
||||
pollStatus.className = 'meta-muted registration-finish-progress';
|
||||
pollStatus.textContent = 'Через несколько секунд начнём проверять подтверждение регистрации.';
|
||||
|
||||
const stopTimers = () => {
|
||||
if (progressTimerId) {
|
||||
window.clearInterval(progressTimerId);
|
||||
progressTimerId = null;
|
||||
}
|
||||
if (pollStartTimerId) {
|
||||
window.clearTimeout(pollStartTimerId);
|
||||
pollStartTimerId = null;
|
||||
}
|
||||
if (pollTimerId) {
|
||||
window.clearInterval(pollTimerId);
|
||||
pollTimerId = null;
|
||||
}
|
||||
if (successTimerId) {
|
||||
window.clearTimeout(successTimerId);
|
||||
successTimerId = null;
|
||||
}
|
||||
};
|
||||
|
||||
const updateVisualProgress = () => {
|
||||
const elapsed = Date.now() - stageStartedAt;
|
||||
let widthPercent = 0;
|
||||
if (elapsed <= REGISTRATION_PROGRESS_DURATION_MS) {
|
||||
const phaseRatio = clamp(elapsed / REGISTRATION_PROGRESS_DURATION_MS, 0, 1);
|
||||
widthPercent = 100 * (1 - ((1 - phaseRatio) ** 1.85));
|
||||
} else {
|
||||
const extraRatio = 1 - Math.exp(-(elapsed - REGISTRATION_PROGRESS_DURATION_MS) / 9000);
|
||||
widthPercent = 88 + (10 * clamp(extraRatio, 0, 1));
|
||||
}
|
||||
progressBar.style.width = `${clamp(widthPercent, 0, 98)}%`;
|
||||
|
||||
if (elapsed < REGISTRATION_POLL_START_DELAY_MS) {
|
||||
progress.textContent = 'Отправили регистрацию в сеть. Даём транзакции несколько секунд на обработку.';
|
||||
return;
|
||||
}
|
||||
if (elapsed < REGISTRATION_CONFIRM_TIMEOUT_MS) {
|
||||
progress.textContent = 'Идёт ожидание подтверждения Solana. Индикатор может замедлиться, это нормально.';
|
||||
return;
|
||||
}
|
||||
progress.textContent = 'Подтверждение затянулось. Продолжаем автоматическую проверку.';
|
||||
};
|
||||
|
||||
const finalizeSuccess = () => {
|
||||
if (stageClosed || successPending) return;
|
||||
successPending = true;
|
||||
stopTimers();
|
||||
progress.textContent = 'Подтверждение получено. Завершаем регистрацию...';
|
||||
pollStatus.textContent = 'Запись уже найдена в Solana, готовим финальный экран.';
|
||||
status.style.display = 'none';
|
||||
progressBar.style.transition = `width ${REGISTRATION_SUCCESS_SETTLE_DELAY_MS}ms ease-out`;
|
||||
progressBar.style.width = '100%';
|
||||
successTimerId = window.setTimeout(() => {
|
||||
if (stageClosed) return;
|
||||
stageClosed = true;
|
||||
renderSolanaDoneStage({ navigate, status, keyBundle, registrationTxId });
|
||||
}, REGISTRATION_SUCCESS_SETTLE_DELAY_MS);
|
||||
};
|
||||
|
||||
const showTimeoutState = () => {
|
||||
if (timeoutShown || stageClosed) return;
|
||||
timeoutShown = true;
|
||||
status.className = 'status-line is-unavailable';
|
||||
status.textContent = 'Подтверждение не пришло вовремя. Возможно, регистрация уже прошла, а возможно ещё нет. Проверьте Tx ID ниже: мы продолжим автоматическую проверку.';
|
||||
status.style.display = '';
|
||||
pollStatus.textContent = 'Пока подтверждения нет. Подождите ещё немного, мы продолжаем проверять регистрацию.';
|
||||
};
|
||||
|
||||
const tryCheckRegistration = async () => {
|
||||
if (stageClosed || confirmationInFlight) return;
|
||||
confirmationInFlight = true;
|
||||
progress.textContent = 'Проверяем подтверждение регистрации в Solana...';
|
||||
status.style.display = timeoutShown ? '' : 'none';
|
||||
try {
|
||||
const result = await checkLoginExistsOnSolana({
|
||||
login: state.registrationDraft.login,
|
||||
solanaEndpoint: state.entrySettings.solanaServer,
|
||||
});
|
||||
if (result?.exists) {
|
||||
finalizeSuccess();
|
||||
return;
|
||||
}
|
||||
pollStatus.textContent = 'Пока ещё не прошла, подождите ещё немного...';
|
||||
if ((Date.now() - stageStartedAt) >= REGISTRATION_CONFIRM_TIMEOUT_MS) {
|
||||
showTimeoutState();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Registration confirmation check failed', toUserMessage(error, 'registration-confirmation'));
|
||||
pollStatus.textContent = 'Проверяем регистрацию повторно...';
|
||||
if ((Date.now() - stageStartedAt) >= REGISTRATION_CONFIRM_TIMEOUT_MS) {
|
||||
showTimeoutState();
|
||||
}
|
||||
} finally {
|
||||
confirmationInFlight = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (headerBackButton) {
|
||||
const replacement = headerBackButton.cloneNode(true);
|
||||
replacement.addEventListener('click', () => {
|
||||
stageClosed = true;
|
||||
stopTimers();
|
||||
navigate('start-view');
|
||||
});
|
||||
headerBackButton.replaceWith(replacement);
|
||||
}
|
||||
|
||||
card.innerHTML = '';
|
||||
status.style.display = 'none';
|
||||
card.append(title, hint, txIdLine, progressWrap, progress, pollStatus, status);
|
||||
|
||||
updateVisualProgress();
|
||||
progressTimerId = window.setInterval(() => {
|
||||
if (stageClosed) return;
|
||||
updateVisualProgress();
|
||||
if ((Date.now() - stageStartedAt) >= REGISTRATION_CONFIRM_TIMEOUT_MS) {
|
||||
showTimeoutState();
|
||||
}
|
||||
}, 160);
|
||||
|
||||
pollStartTimerId = window.setTimeout(() => {
|
||||
if (stageClosed) return;
|
||||
void tryCheckRegistration();
|
||||
pollTimerId = window.setInterval(() => {
|
||||
void tryCheckRegistration();
|
||||
}, REGISTRATION_POLL_INTERVAL_MS);
|
||||
}, REGISTRATION_POLL_START_DELAY_MS);
|
||||
}
|
||||
|
||||
function renderSolanaDoneStage({ navigate, status, keyBundle, registrationTxId = '' }) {
|
||||
const screen = document.querySelector('section.stack');
|
||||
if (!screen) return;
|
||||
@@ -365,7 +561,7 @@ function renderSolanaDoneStage({ navigate, status, keyBundle, registrationTxId =
|
||||
|
||||
const hint = document.createElement('p');
|
||||
hint.className = 'auth-copy registration-finish-text';
|
||||
hint.textContent = 'Подождите 10 секунд, пока обновится транзакция вашей регистрации в блокчейне Solana. После этого вход в аккаунт произойдёт автоматически.';
|
||||
hint.textContent = 'Регистрация подтверждена в блокчейне Solana. Сейчас выполним автоматический вход в аккаунт.';
|
||||
|
||||
const txIdLine = document.createElement('p');
|
||||
txIdLine.className = 'meta-muted registration-finish-tx';
|
||||
|
||||
Reference in New Issue
Block a user