mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 19:15:08 +03:00
Implement login screen
This commit is contained in:
@@ -5,12 +5,9 @@
|
|||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { initializeProfile } from "../userPanel/profile/profile";
|
import type { ErrorResponse, RegisterRequest } from "../core/types";
|
||||||
import type { ErrorResponse, LoginResponse, LoginRequest, RegisterRequest } from "../core/types";
|
|
||||||
import { API_BASE_URL } from "../core/config";
|
import { API_BASE_URL } from "../core/config";
|
||||||
import { showChat, showLogin, showRegister } from "../navigation";
|
import { showLogin } from "../navigation";
|
||||||
import { setUser } from "./api";
|
|
||||||
import { ensureKeysOnLogin } from "./crypto";
|
|
||||||
import { id } from "../utils/utils";
|
import { id } from "../utils/utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,58 +33,6 @@ export function showAlert(containerId: string, message: string, type: "success"
|
|||||||
container.appendChild(alertDiv);
|
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
|
* Handles registration form submission
|
||||||
* @param {Event} e - Form submission event
|
* @param {Event} e - Form submission event
|
||||||
@@ -159,7 +104,6 @@ async function handleRegister(e: Event): Promise<void> {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function init(): void {
|
function init(): void {
|
||||||
id('login-form-element').addEventListener('submit', handleLogin);
|
|
||||||
id('register-form-element').addEventListener('submit', handleRegister);
|
id('register-form-element').addEventListener('submit', handleRegister);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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 { 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() {
|
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 (
|
return (
|
||||||
<AuthContainer>
|
<AuthContainer>
|
||||||
<div className="auth-header">
|
<div className="auth-header">
|
||||||
@@ -12,9 +29,53 @@ export default function LoginScreen() {
|
|||||||
<p>Войдите в свой аккаунт</p>
|
<p>Войдите в свой аккаунт</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="auth-body">
|
<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
|
<mdui-text-field
|
||||||
label="Имя пользователя"
|
label="Имя пользователя"
|
||||||
id="login-username"
|
id="login-username"
|
||||||
@@ -22,7 +83,8 @@ export default function LoginScreen() {
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
icon="person--filled"
|
icon="person--filled"
|
||||||
autocomplete="username"
|
autocomplete="username"
|
||||||
required>
|
required
|
||||||
|
ref={usernameElement}>
|
||||||
</mdui-text-field>
|
</mdui-text-field>
|
||||||
<mdui-text-field
|
<mdui-text-field
|
||||||
label="Пароль"
|
label="Пароль"
|
||||||
@@ -33,7 +95,8 @@ export default function LoginScreen() {
|
|||||||
toggle-password
|
toggle-password
|
||||||
icon="password--filled"
|
icon="password--filled"
|
||||||
autocomplete="current-password"
|
autocomplete="current-password"
|
||||||
required>
|
required
|
||||||
|
ref={passwordElement}>
|
||||||
</mdui-text-field>
|
</mdui-text-field>
|
||||||
|
|
||||||
<mdui-button type="submit">Войти</mdui-button>
|
<mdui-button type="submit">Войти</mdui-button>
|
||||||
@@ -44,7 +107,6 @@ export default function LoginScreen() {
|
|||||||
Ещё нет аккаунта?
|
Ещё нет аккаунта?
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
id="register-link"
|
|
||||||
className="link"
|
className="link"
|
||||||
onClick={showRegister}>
|
onClick={showRegister}>
|
||||||
Зарегистрируйтесь
|
Зарегистрируйтесь
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"tweetnacl": "^1.0.3",
|
"tweetnacl": "^1.0.3",
|
||||||
|
"use-immer": "^0.11.0",
|
||||||
"zustand": "^5.0.8"
|
"zustand": "^5.0.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user