Implement login screen

This commit is contained in:
2025-08-30 10:32:14 +03:00
Unverified
parent 283ed18bc9
commit 6f9c9a6b73
4 changed files with 97 additions and 74 deletions
+2 -58
View File
@@ -5,12 +5,9 @@
* @version 1.0.0
*/
import { initializeProfile } from "../userPanel/profile/profile";
import type { ErrorResponse, LoginResponse, LoginRequest, RegisterRequest } from "../core/types";
import type { ErrorResponse, RegisterRequest } from "../core/types";
import { API_BASE_URL } from "../core/config";
import { showChat, showLogin, showRegister } from "../navigation";
import { setUser } from "./api";
import { ensureKeysOnLogin } from "./crypto";
import { showLogin } from "../navigation";
import { id } from "../utils/utils";
/**
@@ -36,58 +33,6 @@ export function showAlert(containerId: string, message: string, type: "success"
container.appendChild(alertDiv);
}
/**
* Handles login form submission
* @param {Event} e - Form submission event
*/
async function handleLogin(e: Event): Promise<void> {
e.preventDefault();
const usernameElement = id<HTMLInputElement>('login-username');
const passwordElement = id<HTMLInputElement>('login-password');
const username = usernameElement.value.trim();
const password = passwordElement.value.trim();
if (!username || !password) {
showAlert('login-alerts', 'Пожалуйста, заполните все поля', 'danger');
return;
}
try {
const request: LoginRequest = {
username: username,
password: password
}
const response = await fetch(`${API_BASE_URL}/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(request)
});
if (response.ok) {
const data: LoginResponse = await response.json();
// Store the JWT token
setUser(data.token, data.user)
try {
await ensureKeysOnLogin(password);
} catch (e) {
console.error("Key setup failed:", e);
}
showChat();
initializeProfile(); // Initialize profile after login
} else {
const data: ErrorResponse = await response.json();
showAlert('login-alerts', data.message || 'Неверное имя пользователя или пароль', 'danger');
}
} catch (error) {
showAlert('login-alerts', 'Ошибка соединения с сервером', 'danger');
}
}
/**
* Handles registration form submission
* @param {Event} e - Form submission event
@@ -159,7 +104,6 @@ async function handleRegister(e: Event): Promise<void> {
* @private
*/
function init(): void {
id('login-form-element').addEventListener('submit', handleLogin);
id('register-form-element').addEventListener('submit', handleRegister);
}
+16
View File
@@ -0,0 +1,16 @@
export type AlertType = "success" | "danger"
export interface Alert {
type: AlertType;
message: string;
}
export function AlertsContainer({ alerts }: { alerts: Alert[]}) {
return (
<div>
{alerts.map(alert => {
return <div className={`alert alert-${alert.type}`}>{alert.message}</div>
})}
</div>
)
}
+68 -6
View File
@@ -1,7 +1,24 @@
import { showLogin, showRegister } from "../../navigation";
import { useImmer } from "use-immer";
import { showChat, showRegister } from "../../navigation";
import { AlertsContainer, type Alert, type AlertType } from "../components/Alerts";
import { AuthContainer } from "../components/Auth";
import type { ErrorResponse, LoginRequest, LoginResponse } from "../../core/types";
import { setUser } from "../../auth/api";
import { ensureKeysOnLogin } from "../../auth/crypto";
import { API_BASE_URL } from "../../core/config";
import { initializeProfile } from "../../userPanel/profile/profile";
import { useRef } from "react";
export default function LoginScreen() {
const [alerts, updateAlerts] = useImmer<Alert[]>([]);
function showAlert(type: AlertType, message: string) {
updateAlerts((alerts) => alerts.push({type: type, message: message}));
}
const usernameElement = useRef<HTMLInputElement>(null);
const passwordElement = useRef<HTMLInputElement>(null);
return (
<AuthContainer>
<div className="auth-header">
@@ -12,9 +29,53 @@ export default function LoginScreen() {
<p>Войдите в свой аккаунт</p>
</div>
<div className="auth-body">
<div id="login-alerts"></div>
<AlertsContainer alerts={alerts} />
<form id="login-form-element">
<form
onSubmit={async (e) => {
e.preventDefault();
const username = usernameElement.current!.value.trim();
const password = passwordElement.current!.value.trim();
if (!username || !password) {
showAlert("danger", "Пожалуйста, заполните все поля");
return;
}
try {
const request: LoginRequest = {
username: username,
password: password
}
const response = await fetch(`${API_BASE_URL}/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(request)
});
if (response.ok) {
const data: LoginResponse = await response.json();
// Store the JWT token
setUser(data.token, data.user)
try {
await ensureKeysOnLogin(password);
} catch (e) {
console.error("Key setup failed:", e);
}
showChat();
initializeProfile(); // Initialize profile after login
} else {
const data: ErrorResponse = await response.json();
showAlert("danger", data.message || "Неверное имя пользователя или пароль");
}
} catch (error) {
showAlert("danger", "Ошибка соединения с сервером");
}
}}>
<mdui-text-field
label="Имя пользователя"
id="login-username"
@@ -22,7 +83,8 @@ export default function LoginScreen() {
variant="outlined"
icon="person--filled"
autocomplete="username"
required>
required
ref={usernameElement}>
</mdui-text-field>
<mdui-text-field
label="Пароль"
@@ -33,7 +95,8 @@ export default function LoginScreen() {
toggle-password
icon="password--filled"
autocomplete="current-password"
required>
required
ref={passwordElement}>
</mdui-text-field>
<mdui-button type="submit">Войти</mdui-button>
@@ -44,7 +107,6 @@ export default function LoginScreen() {
Ещё нет аккаунта?
<a
href="#"
id="register-link"
className="link"
onClick={showRegister}>
Зарегистрируйтесь
+1
View File
@@ -51,6 +51,7 @@
"react": "^19.1.1",
"react-dom": "^19.1.1",
"tweetnacl": "^1.0.3",
"use-immer": "^0.11.0",
"zustand": "^5.0.8"
}
}