Files
SHiNE-server/shine-UI/js/pages/login-view.js
T

177 lines
6.3 KiB
JavaScript

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;
}