Refactor frontend to hook-based architecture

Extract logic from god components into custom hooks (useAuthCheck,
useConversations, useChat, usePresignedUrl, useAdminUsers, useOIDCAuth).
Eliminate unnecessary useEffects per React guidelines — scroll is now
imperative, isAdmin comes from useAuthCheck instead of a separate fetch.
ConversationList becomes a pure presentational component. Wrap bubble
components in React.memo.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-24 09:11:57 -04:00
parent bac773ae4b
commit 4ac0754ea7
14 changed files with 495 additions and 371 deletions
+3 -38
View File
@@ -1,48 +1,13 @@
import { useState, useEffect } from "react";
import "./App.css";
import { AuthProvider } from "./contexts/AuthContext";
import { ChatScreen } from "./components/ChatScreen";
import { LoginScreen } from "./components/LoginScreen";
import { conversationService } from "./api/conversationService";
import { useAuthCheck } from "./hooks/useAuthCheck";
import catIcon from "./assets/cat.png";
const AppContainer = () => {
const [isAuthenticated, setAuthenticated] = useState<boolean>(false);
const [isChecking, setIsChecking] = useState<boolean>(true);
const { isAuthenticated, isChecking, isAdmin, setAuthenticated } = useAuthCheck();
useEffect(() => {
const checkAuth = async () => {
const accessToken = localStorage.getItem("access_token");
const refreshToken = localStorage.getItem("refresh_token");
// No tokens at all, not authenticated
if (!accessToken && !refreshToken) {
setIsChecking(false);
setAuthenticated(false);
return;
}
// Try to verify token by making a request
try {
await conversationService.getAllConversations();
// If successful, user is authenticated
setAuthenticated(true);
} catch (error) {
// Token is invalid or expired
console.error("Authentication check failed:", error);
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
setAuthenticated(false);
} finally {
setIsChecking(false);
}
};
checkAuth();
}, []);
// Show loading state while checking authentication
if (isChecking) {
return (
<div className="h-screen flex flex-col items-center justify-center bg-cream gap-4">
@@ -61,7 +26,7 @@ const AppContainer = () => {
return (
<>
{isAuthenticated ? (
<ChatScreen setAuthenticated={setAuthenticated} />
<ChatScreen setAuthenticated={setAuthenticated} isAdmin={isAdmin} />
) : (
<LoginScreen setAuthenticated={setAuthenticated} />
)}