import { renderHeader } from '../components/header.js';
import { authService, clearAuthMessages, state } from '../state.js';
import { toUserMessage } from '../services/ui-error-texts.js';
export const pageMeta = { id: 'register-view', title: 'Зарегистрироваться', showAppChrome: false };
export function render({ navigate }) {
const screen = document.createElement('section');
screen.className = 'stack';
clearAuthMessages();
const form = document.createElement('div');
form.className = 'card stack';
const loginInput = document.createElement('input');
loginInput.className = 'input';
loginInput.type = 'text';
loginInput.value = state.registrationDraft.login;
loginInput.placeholder = 'Введите логин';
const passwordInput = document.createElement('input');
passwordInput.className = 'input';
passwordInput.type = 'password';
passwordInput.value = state.registrationDraft.password;
passwordInput.placeholder = 'Введите пароль (можно оставить пустым)';
const statusText = document.createElement('p');
statusText.className = 'meta-muted';
statusText.textContent = 'Проверка логина: не выполнена';
const formError = document.createElement('p');
formError.className = 'status-line is-unavailable';
formError.style.display = 'none';
const advanced = document.createElement('details');
advanced.className = 'card stack';
advanced.innerHTML = `
Расширенные
Схема derivation ключей: при непустом пароле используется Argon2id (средний профиль), затем из результата строится секрет для root/bch/dev ключей.
В derivation участвуют и логин, и пароль: одинаковый пароль у разных логинов даёт разные ключи.
Если пароль пустой — используется прежний тестовый режим совместимости (старый детерминированный вариант).
Для тесто оставьте пустой пароль.
Профиль Argon2id сейчас фиксированный: t=3, m=262144 KiB (256 MB), p=1, dkLen=32. Выбор уровня будет добавлен позже.
`;
const checkButton = document.createElement('button');
checkButton.className = 'ghost-btn';
checkButton.type = 'button';
checkButton.textContent = 'Проверить логин';
async function runAvailabilityCheck() {
const login = loginInput.value.trim();
if (!login) {
statusText.textContent = 'Введите логин';
formError.style.display = 'none';
return false;
}
checkButton.disabled = true;
checkButton.textContent = 'Проверка...';
try {
await authService.reconnect(state.entrySettings.shineServer);
const isFree = await authService.ensureLoginFree(login);
statusText.textContent = isFree ? 'Логин свободен ✅' : 'Логин уже занят ❌';
statusText.className = isFree ? 'is-available' : 'is-unavailable';
formError.style.display = 'none';
return isFree;
} catch (error) {
statusText.textContent = toUserMessage(error, 'Не удалось проверить логин');
statusText.className = 'is-unavailable';
return false;
} finally {
checkButton.disabled = false;
checkButton.textContent = 'Проверить логин';
}
}
checkButton.addEventListener('click', runAvailabilityCheck);
form.innerHTML = `
`;
form.children[0].append(loginInput);
form.children[1].append(passwordInput);
form.append(checkButton, statusText, advanced, formError);
const actions = document.createElement('div');
actions.className = 'auth-footer-actions';
const backButton = document.createElement('button');
backButton.className = 'ghost-btn';
backButton.type = 'button';
backButton.textContent = 'Назад';
backButton.addEventListener('click', () => navigate('start-view'));
const nextButton = document.createElement('button');
nextButton.className = 'primary-btn';
nextButton.type = 'button';
nextButton.textContent = 'Далее';
nextButton.addEventListener('click', async () => {
formError.style.display = 'none';
const isFree = await runAvailabilityCheck();
if (!isFree) return;
state.registrationDraft.login = loginInput.value.trim();
state.registrationDraft.password = passwordInput.value;
state.registrationDraft.preGeneratedKeyBundle = null;
// Показываем информационный экран пока генерируются ключи
form.innerHTML = '';
const infoMsg = document.createElement('p');
infoMsg.className = 'auth-copy';
infoMsg.textContent =
'Из вашего логина и пароля (надеемся, что вы выбрали достаточно длинный и надёжный пароль) ' +
'генерируется секрет, из которого получаются root key, blockchain key и device key.';
const spinnerMsg = document.createElement('p');
spinnerMsg.className = 'meta-muted';
spinnerMsg.textContent = 'Генерация ключей...';
const genError = document.createElement('p');
genError.className = 'status-line is-unavailable';
genError.style.display = 'none';
form.append(infoMsg, spinnerMsg, genError);
nextButton.disabled = true;
backButton.disabled = true;
try {
const keyBundle = await authService.derivePasswordKeyBundle(
state.registrationDraft.login,
state.registrationDraft.password,
);
state.registrationDraft.preGeneratedKeyBundle = keyBundle;
navigate('registration-payment-view');
} catch (error) {
genError.textContent = `Ошибка генерации ключей: ${error?.message || 'неизвестная ошибка'}`;
genError.style.display = '';
spinnerMsg.style.display = 'none';
nextButton.disabled = false;
backButton.disabled = false;
}
});
actions.append(backButton, nextButton);
screen.append(
renderHeader({
title: 'Зарегистрироваться',
leftAction: { label: '←', onClick: () => navigate('start-view') },
}),
form,
actions,
);
return screen;
}