import { renderHeader } from '../components/header.js'; import { authService, cancelAddProfileLogin, clearAuthMessages, setAuthBusy, setAuthError, state, } from '../state.js'; import { buildShineHttpUrlFromAddress } from '../services/shine-server-resolver.js'; import { toUserMessage } from '../services/ui-error-texts.js'; export const pageMeta = { id: 'login-view', title: 'Войти', showAppChrome: false }; function setStatus(statusEl, message, kind = 'error') { statusEl.classList.toggle('is-unavailable', kind === 'error'); statusEl.classList.toggle('is-available', kind !== 'error'); statusEl.textContent = message; statusEl.style.display = message ? '' : 'none'; } function setRemoteServer(remoteWrap, serverLoginEl, serverLinkEl, payload) { const serverLogin = String(payload?.accessServerLogin || '').trim(); const serverUrl = String(payload?.accessServerUrl || '').trim(); serverLoginEl.textContent = serverLogin ? `@${serverLogin}` : 'другой сервер доступа'; serverLinkEl.textContent = serverUrl || 'Открыть сервер'; serverLinkEl.href = buildShineHttpUrlFromAddress(serverUrl); remoteWrap.style.display = ''; } export function render({ navigate }) { const screen = document.createElement('section'); screen.className = 'stack auth-screen auth-screen--lower login-choice-screen'; clearAuthMessages(); const panel = document.createElement('section'); panel.className = 'login-panel stack'; const title = document.createElement('h1'); title.className = 'login-panel-title'; title.textContent = 'Введите логин'; const loginField = document.createElement('label'); loginField.className = 'stack'; const loginInput = document.createElement('input'); loginInput.className = 'input'; loginInput.type = 'text'; loginInput.autocomplete = 'username'; loginInput.autocapitalize = 'off'; loginInput.spellcheck = false; loginInput.placeholder = 'Логин'; loginInput.value = String(state.loginDraft.login || ''); loginField.append(loginInput); const status = document.createElement('p'); status.className = 'status-line is-unavailable'; status.style.display = 'none'; const remoteWrap = document.createElement('div'); remoteWrap.className = 'login-remote-server stack'; remoteWrap.style.display = 'none'; const remoteText = document.createElement('p'); remoteText.className = 'auth-copy'; const serverLoginEl = document.createElement('strong'); const textBefore = document.createTextNode('Этот пользователь SHiNE зарегистрирован на другом сервере доступа: '); remoteText.append(textBefore, serverLoginEl, document.createTextNode('. Для входа перейдите на его сервер.')); const serverLinkEl = document.createElement('a'); serverLinkEl.className = 'primary-btn login-server-link'; serverLinkEl.target = '_self'; serverLinkEl.rel = 'noopener'; remoteWrap.append(remoteText, serverLinkEl); const passwordButton = document.createElement('button'); passwordButton.className = 'primary-btn'; passwordButton.type = 'button'; passwordButton.textContent = 'Войти по паролю'; const deviceButton = document.createElement('button'); deviceButton.className = 'ghost-btn'; deviceButton.type = 'button'; deviceButton.textContent = 'Войти через другое устройство'; const actions = document.createElement('div'); actions.className = 'auth-actions login-actions-wide'; actions.append(passwordButton, deviceButton); const resolveAndContinue = async (targetPage) => { const login = String(loginInput.value || '').trim(); setStatus(status, ''); remoteWrap.style.display = 'none'; if (!login) { setStatus(status, 'Введите логин.'); loginInput.focus(); return; } passwordButton.disabled = true; deviceButton.disabled = true; setAuthBusy(true); setAuthError(''); try { await authService.reconnect(state.entrySettings.shineServer); const resolved = await authService.resolveLoginForAuth(login); const resolution = String(resolved?.resolution || '').trim().toUpperCase(); if (resolution === 'NOT_FOUND') { setStatus(status, 'Пользователь с таким логином не зарегистрирован в SHiNE.'); return; } if (resolution === 'NO_ACCESS_SERVER') { setStatus(status, 'Пользователь зарегистрирован в SHiNE, но для него не найден действующий сервер доступа.'); return; } if (resolution === 'REMOTE') { state.loginDraft.login = String(resolved?.login || login).trim(); setRemoteServer(remoteWrap, serverLoginEl, serverLinkEl, resolved); return; } if (resolution !== 'LOCAL') { setStatus(status, 'Сервер вернул неизвестный статус проверки логина.'); return; } state.loginDraft.login = String(resolved?.login || login).trim(); state.loginDraft.password = ''; navigate(targetPage); } catch (error) { const message = toUserMessage(error, 'Не удалось проверить логин.'); setAuthError(message); setStatus(status, message); } finally { setAuthBusy(false); passwordButton.disabled = false; deviceButton.disabled = false; } }; passwordButton.addEventListener('click', () => resolveAndContinue('login-password-view')); deviceButton.addEventListener('click', () => resolveAndContinue('login-other-device-view')); loginInput.addEventListener('keydown', (event) => { if (event.key === 'Enter') { event.preventDefault(); void resolveAndContinue('login-password-view'); } }); panel.append(title, loginField, status, remoteWrap, actions); screen.append( renderHeader({ title: '', leftAction: { label: '←', onClick: async () => { if (state.authReturnHash === '/profiles') { await cancelAddProfileLogin(); navigate('profiles-view'); return; } navigate('start-view'); }, }, }), panel, ); window.setTimeout(() => loginInput.focus(), 0); return screen; }