Улучшен вход пользователя в аккаунт

This commit is contained in:
AidarKC
2026-08-28 16:51:14 +04:00
parent f2fdb8195c
commit 357a05f7ec
25 changed files with 821 additions and 270 deletions
+143 -17
View File
@@ -1,31 +1,156 @@
import { renderHeader } from '../components/header.js';
import {
authService,
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';
const loginButton = document.createElement('button');
loginButton.className = 'ghost-btn';
loginButton.type = 'button';
loginButton.textContent = 'Войти по паролю';
loginButton.addEventListener('click', () => navigate('login-password-view'));
const otherDeviceButton = document.createElement('button');
otherDeviceButton.className = 'ghost-btn';
otherDeviceButton.type = 'button';
otherDeviceButton.textContent = 'Войти через другое устройство';
otherDeviceButton.addEventListener('click', () => navigate('login-other-device-view'));
const actions = document.createElement('div');
actions.className = 'auth-actions login-actions-wide';
actions.append(loginButton, otherDeviceButton);
clearAuthMessages();
const panel = document.createElement('section');
panel.className = 'login-panel stack';
panel.innerHTML = '<h1 class="login-panel-title">Войти</h1>';
panel.append(actions);
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({
@@ -35,5 +160,6 @@ export function render({ navigate }) {
panel,
);
window.setTimeout(() => loginInput.focus(), 0);
return screen;
}