import { useRef, useState } from "react"; import { useNavigate } from "react-router-dom"; import { motion, type Transition, type Variants } from "motion/react"; import { useImmer } from "use-immer"; import type { LoginRequest } from "@/core/types"; import { useUserStore } from "@/state/user"; import { MaterialButton } from "@/utils/material"; import api from "@/core/api"; import { AuthTextField, type AuthTextFieldHandle } from "./AuthTextField"; import { initialize, isSupported, subscribe } from "@/core/push-notifications/push-notifications"; import type { Alert, AlertType } from "./Auth"; import { AuthHeader, AlertsContainer } from "./Auth"; import styles from "./auth.module.scss"; import { ensureAuthenticated } from "@/core/websocket"; const loginFieldVariants: Variants = { initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 } }; const loginFieldTransition: Transition = { duration: 0.3, ease: "easeInOut" }; const loginButtonVariants: Variants = { initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 } }; const loginButtonTransition: Transition = { duration: 0.3, delay: 0.4, ease: "easeInOut" }; interface LoginFormProps { onSwitchMode: () => void; } export function LoginForm({ onSwitchMode }: LoginFormProps) { const [isLoading, setIsLoading] = useState(false); const [alerts, updateAlerts] = useImmer([]); const setUser = useUserStore(state => state.setUser); const navigate = useNavigate(); function showAlert(type: AlertType, message: string) { updateAlerts((alerts) => { alerts.push({type: type, message: message}) }); } const usernameElement = useRef(null); const passwordElement = useRef(null); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); if (isLoading) return; const username = usernameElement.current!.value.trim(); const password = passwordElement.current!.value.trim(); if (!username || !password) { showAlert("danger", "Пожалуйста, заполните все поля"); return; } setIsLoading(true); try { const derived = await api.user.auth.deriveAuthSecret(username, password); const request: LoginRequest = { username: username, password: derived } try { const data = await api.user.auth.login(request); setUser(data.token, data.user); try { await api.user.auth.ensureKeysOnLogin(password, data.token); } catch (e) { console.error("Key setup failed:", e); try { await api.user.auth.syncPublicKeyToServerIfMissing(data.token); } catch (e2) { console.error("Public key re-sync failed:", e2); } } // Ensure WebSocket is connected and authenticated try { await ensureAuthenticated(); } catch (e) { console.error("WebSocket authentication failed:", e); } navigate("/chat"); try { if (isSupported()) { const initialized = await initialize(); if (initialized) { await subscribe(data.token); console.log("Notifications enabled"); } else { console.log("Notification permission denied"); } } else { console.log("Notifications not supported"); } } catch (e) { console.error("Notification setup failed:", e); } } catch (error: any) { if (error.message && error.message.includes("suspension")) { const setSuspended = useUserStore.getState().setSuspended; setSuspended(error.message || "No reason provided"); return; } showAlert("danger", error.message || "Неверное имя пользователя или пароль"); } } catch (error: any) { showAlert("danger", error.message || "Ошибка соединения с сервером"); } finally { setIsLoading(false); } } return ( <>
{isLoading ? "Вход..." : "Войти"}

Ещё нет аккаунта? { e.preventDefault(); onSwitchMode(); }}> Зарегистрируйтесь

); }