From de2735eb16af52a78d81c86b816db9644bb7cfa7 Mon Sep 17 00:00:00 2001 From: d3m0k1d Date: Sun, 15 Feb 2026 00:06:22 +0300 Subject: [PATCH] fix: bug with avatar fixes --- frontend/package-lock.json | 8 +- frontend/package.json | 2 +- frontend/src/App.tsx | 45 ++++--- frontend/src/components/AuthCallback.tsx | 37 +++--- frontend/src/components/Navigation.tsx | 162 +++++++++-------------- frontend/src/contexts/AuthContext.tsx | 86 ++++++++++++ 6 files changed, 197 insertions(+), 143 deletions(-) create mode 100644 frontend/src/contexts/AuthContext.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c37e9db..16d3c49 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -17,7 +17,7 @@ "devDependencies": { "@eslint/js": "^9.39.1", "@types/node": "^24.10.1", - "@types/react": "^19.2.5", + "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.1", "daisyui": "^5.5.14", @@ -1640,9 +1640,9 @@ } }, "node_modules/@types/react": { - "version": "19.2.10", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", - "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "dev": true, "license": "MIT", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index aedf76a..b6a8cfd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,7 +19,7 @@ "devDependencies": { "@eslint/js": "^9.39.1", "@types/node": "^24.10.1", - "@types/react": "^19.2.5", + "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.1", "daisyui": "^5.5.14", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ca3d92c..4f79660 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,6 +1,7 @@ import "./App.css"; import { BrowserRouter, Routes, Route } from "react-router-dom"; import { useEffect } from "react"; +import { AuthProvider } from "./contexts/AuthContext.tsx"; import Navigation from "./components/Navigation.tsx"; import Footer from "./components/Footer.tsx"; import AuthCallback from "./components/AuthCallback.tsx"; @@ -14,27 +15,29 @@ function App() { }, []); return ( - -
- -
- - - - - - } - /> - } /> - } /> - -
-
-
-
+ + +
+ +
+ + + + + + } + /> + } /> + } /> + +
+
+
+
+
); } diff --git a/frontend/src/components/AuthCallback.tsx b/frontend/src/components/AuthCallback.tsx index 8fce776..114936c 100644 --- a/frontend/src/components/AuthCallback.tsx +++ b/frontend/src/components/AuthCallback.tsx @@ -1,29 +1,36 @@ import { useEffect } from "react"; import { useNavigate } from "react-router-dom"; +import { useAuth } from "../contexts/AuthContext.tsx"; export default function AuthCallback() { const navigate = useNavigate(); + const { checkAuth } = useAuth(); useEffect(() => { - const hash = window.location.hash.substring(1); - const params = new URLSearchParams(hash); - const token = params.get("token"); + const processAuth = async () => { + const hash = window.location.hash.substring(1); + const params = new URLSearchParams(hash); + const token = params.get("token"); - if (token) { - localStorage.setItem("auth_token", token); + if (token) { + localStorage.setItem("auth_token", token); + console.log("Token saved, loading user..."); - navigate("/"); - } else { - navigate("/login?error=no_token"); - } - }, [navigate]); + await checkAuth(); + + navigate("/", { replace: true }); + } else { + console.error("No token in URL"); + navigate("/login?error=no_token"); + } + }; + + processAuth(); + }, [navigate, checkAuth]); return ( -
-
-
-

Completing authentication...

-
+
+
); } diff --git a/frontend/src/components/Navigation.tsx b/frontend/src/components/Navigation.tsx index 8dffb50..068f5c5 100644 --- a/frontend/src/components/Navigation.tsx +++ b/frontend/src/components/Navigation.tsx @@ -1,58 +1,15 @@ -import { useState, useEffect } from "react"; +import { useState } from "react"; +import { useAuth } from "../contexts/AuthContext.tsx"; -interface User { - name?: string; - email?: string; - avatar?: string; -} - -export default function Navigation() { - const [isOpen, setIsOpen] = useState(false); - const [user, setUser] = useState(null); - const [isLoading, setIsLoading] = useState(true); - - useEffect(() => { - checkAuth(); - }, []); - - const checkAuth = async () => { - try { - const token = localStorage.getItem("auth_token"); - - if (!token) { - setIsLoading(false); - return; - } - - const response = await fetch("/api/v1/session", { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - - if (response.ok) { - const data = await response.json(); - setUser(data.user); - console.log("User loaded:", data.user); - } else { - console.error("Token invalid, removing"); - localStorage.removeItem("auth_token"); - } - } catch (error) { - console.error("Auth check failed:", error); - localStorage.removeItem("auth_token"); - } finally { - setIsLoading(false); - } - }; +function AccountAvatar() { + const { user, isLoading, logout } = useAuth(); const handleLogout = () => { - localStorage.removeItem("auth_token"); - setUser(null); + logout(); window.location.href = "/"; }; - const getInitials = (user: User): string => { + const getInitials = (user: { name?: string; email?: string }): string => { if (user.name) { return user.name.substring(0, 2).toUpperCase(); } @@ -62,70 +19,73 @@ export default function Navigation() { return "?"; }; - const AccountAvatar = () => { - if (isLoading) { - return ( -
- ); - } + if (isLoading) { + return
; + } - return ( - <> - {user ? ( -
- {user.avatar ? ( - {user.name - ) : ( -
- {getInitials(user)} -
- )} - {/* Tooltip при наведении */} -
- Click to logout + return ( + <> + {user ? ( +
+ {user.avatar ? ( + {user.name + ) : ( +
+ {getInitials(user)}
+ )} +
+ Click to logout
- ) : ( - + ) : ( + + - - - - - )} - - ); + + + + )} + + ); +} + +export default function Navigation() { + const [isOpen, setIsOpen] = useState(false); + const { user, logout } = useAuth(); + + const handleLogout = () => { + logout(); + window.location.href = "/"; }; return ( <> - {/* Account Avatar - Fixed position, synced with nav */}
- {/* Mobile Menu */} {isOpen && ( <>
Promise; + logout: () => void; +} + +const AuthContext = createContext(undefined); + +export function AuthProvider({ children }: { children: ReactNode }) { + const [user, setUser] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + const checkAuth = async () => { + setIsLoading(true); + try { + const token = localStorage.getItem("auth_token"); + + if (!token) { + setUser(null); + setIsLoading(false); + return; + } + + const response = await fetch("/api/session", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (response.ok) { + const data = await response.json(); + setUser(data.user); + console.log("User loaded:", data.user); + } else { + console.error("Token invalid, removing"); + localStorage.removeItem("auth_token"); + setUser(null); + } + } catch (error) { + console.error("Auth check failed:", error); + localStorage.removeItem("auth_token"); + setUser(null); + } finally { + setIsLoading(false); + } + }; + + const logout = () => { + localStorage.removeItem("auth_token"); + setUser(null); + }; + + useEffect(() => { + checkAuth(); + }, []); + + return ( + + {children} + + ); +} + +// eslint-disable-next-line react-refresh/only-export-components +export function useAuth() { + const context = useContext(AuthContext); + if (!context) { + throw new Error("useAuth must be used within AuthProvider"); + } + return context; +} -- 2.52.0