import { renderHeader } from '../components/header.js';
import { authService, setAuthError, setAuthInfo, state } from '../state.js';
import { deriveEspPairingPasswordHash } from '../services/device-pairing-service.js';
import { toUserMessage } from '../services/ui-error-texts.js';
export const pageMeta = { id: 'trusted-device-login-settings-view', title: 'Настройки входа через устройство' };
function setStatus(statusEl, message, kind = 'info') {
statusEl.classList.toggle('is-unavailable', kind === 'error');
statusEl.classList.toggle('is-available', kind !== 'error');
statusEl.textContent = message;
statusEl.style.display = message ? '' : 'none';
}
function describeState(settings) {
if (!settings?.enabled) return 'Вход через другое устройство запрещён.';
if (settings?.hasPassword) return 'Вход через другое устройство разрешён только с дополнительным паролем.';
return 'Вход через другое устройство разрешён без дополнительного пароля.';
}
export function render({ navigate }) {
const screen = document.createElement('section');
screen.className = 'stack';
const card = document.createElement('div');
card.className = 'card stack';
const summary = document.createElement('p');
summary.className = 'auth-copy';
summary.textContent = 'Загружаем текущие настройки...';
const hint = document.createElement('p');
hint.className = 'meta-muted';
hint.textContent = 'Дополнительный пароль не даёт права на вход сам по себе. Он только отсекает лишние заявки до подтверждения на доверенном устройстве.';
const status = document.createElement('p');
status.className = 'status-line is-unavailable';
status.style.display = 'none';
const actions = document.createElement('div');
actions.className = 'row';
actions.style.flexWrap = 'wrap';
const enableToggleBtn = document.createElement('button');
enableToggleBtn.className = 'primary-btn';
enableToggleBtn.type = 'button';
const noPasswordBtn = document.createElement('button');
noPasswordBtn.className = 'ghost-btn';
noPasswordBtn.type = 'button';
noPasswordBtn.textContent = 'Сделать вход без пароля';
const passwordForm = document.createElement('div');
passwordForm.className = 'stack';
passwordForm.innerHTML = `
`;
const passwordInput = passwordForm.querySelector('#trusted-login-password');
const passwordConfirmInput = passwordForm.querySelector('#trusted-login-password-confirm');
const savePasswordBtn = passwordForm.querySelector('#trusted-login-password-save');
card.append(summary, hint, actions, passwordForm, status);
let settings = { enabled: true, hasPassword: false };
let busy = false;
const setBusy = (flag) => {
busy = flag;
enableToggleBtn.disabled = flag;
noPasswordBtn.disabled = flag || !settings.enabled || !settings.hasPassword;
savePasswordBtn.disabled = flag;
passwordInput.disabled = flag;
passwordConfirmInput.disabled = flag;
};
const renderUi = () => {
summary.textContent = describeState(settings);
enableToggleBtn.textContent = settings.enabled
? 'Запретить вход через другое устройство'
: 'Разрешить вход через другое устройство';
actions.innerHTML = '';
actions.append(enableToggleBtn);
if (settings.enabled) {
actions.append(noPasswordBtn);
}
passwordForm.style.display = settings.enabled ? '' : 'none';
noPasswordBtn.disabled = busy || !settings.hasPassword;
setBusy(busy);
};
const reloadSettings = async () => {
settings = await authService.getTrustedDeviceLoginSettings();
renderUi();
};
enableToggleBtn.addEventListener('click', async () => {
setStatus(status, '', 'info');
setBusy(true);
try {
settings = await authService.upsertTrustedDeviceLoginSettings({
enabled: !settings.enabled,
passwordHash: '',
});
renderUi();
setAuthInfo(settings.enabled
? 'Вход через другое устройство разрешён.'
: 'Вход через другое устройство запрещён.');
setStatus(status, describeState(settings), 'info');
} catch (error) {
const message = toUserMessage(error, 'Не удалось изменить режим входа.');
setAuthError(message);
setStatus(status, message, 'error');
} finally {
setBusy(false);
}
});
noPasswordBtn.addEventListener('click', async () => {
setStatus(status, '', 'info');
setBusy(true);
try {
settings = await authService.upsertTrustedDeviceLoginSettings({
enabled: true,
passwordHash: '',
});
renderUi();
setAuthInfo('Вход через другое устройство теперь работает без дополнительного пароля.');
setStatus(status, 'Вход теперь работает без дополнительного пароля.', 'info');
} catch (error) {
const message = toUserMessage(error, 'Не удалось убрать дополнительный пароль.');
setAuthError(message);
setStatus(status, message, 'error');
} finally {
setBusy(false);
}
});
savePasswordBtn.addEventListener('click', async () => {
setStatus(status, '', 'info');
const password = String(passwordInput.value || '');
const confirm = String(passwordConfirmInput.value || '');
if (!password || !confirm) {
setStatus(status, 'Заполните пароль и подтверждение.', 'error');
return;
}
if (password !== confirm) {
setStatus(status, 'Пароли не совпадают.', 'error');
return;
}
setBusy(true);
try {
const finalHash = await deriveEspPairingPasswordHash(
String(state.session.login || ''),
password,
);
settings = await authService.upsertTrustedDeviceLoginSettings({
enabled: true,
passwordHash: finalHash,
});
passwordInput.value = '';
passwordConfirmInput.value = '';
renderUi();
setAuthInfo('Дополнительный пароль для входа через другое устройство сохранён.');
setStatus(status, 'Дополнительный пароль сохранён.', 'info');
} catch (error) {
const message = toUserMessage(error, 'Не удалось сохранить дополнительный пароль.');
setAuthError(message);
setStatus(status, message, 'error');
} finally {
setBusy(false);
}
});
screen.append(
renderHeader({
title: 'Настройки входа через устройство',
leftAction: { label: '←', onClick: () => navigate('device-pairing-view') },
}),
card,
);
void reloadSettings().catch((error) => {
const message = toUserMessage(error, 'Не удалось загрузить настройки входа через устройство.');
setAuthError(message);
setStatus(status, message, 'error');
});
return screen;
}