import React, { useState, useEffect } from 'react'; import { Routes, Route, useNavigate, useParams, useLocation } from 'react-router-dom'; import { Book, ChevronRight, Moon, Sun, LogOut, Search } from 'lucide-react'; import BookSelector from './components/BookSelector'; import ChapterSelector from './components/ChapterSelector'; import BibleReader from './components/BibleReader'; import FavoritesMenu from './components/FavoritesMenu'; import SearchComponent from './components/SearchComponent'; import { getBooks } from './services/api'; interface BookData { books: string[]; } function App() { const [books, setBooks] = useState([]); const [loading, setLoading] = useState(true); const [user, setUser] = useState(null); const [authAvailable, setAuthAvailable] = useState(false); const [darkMode, setDarkMode] = useState(() => { // Load dark mode preference from localStorage as fallback const saved = localStorage.getItem('darkMode'); return saved ? JSON.parse(saved) : false; }); const [error, setError] = useState(''); const [showSearch, setShowSearch] = useState(false); const [selectedVersion, setSelectedVersion] = useState('esv'); // Default to ESV const [availableVersions, setAvailableVersions] = useState([]); const location = useLocation(); const navigate = useNavigate(); // Debug logging console.log('App component rendered'); useEffect(() => { console.log('App useEffect triggered'); loadVersions(); loadBooks(); checkAuthStatus(); }, []); // Load versions when version changes useEffect(() => { loadBooks(); }, [selectedVersion]); // Load user preferences when user changes useEffect(() => { if (user) { loadUserPreferences(); } }, [user]); // Load user preferences from database const loadUserPreferences = async () => { if (!user) return; try { const response = await fetch('/api/preferences', { credentials: 'include' }); if (response.ok) { const preferences = await response.json(); console.log('Loaded user preferences:', preferences); setDarkMode(preferences.dark_mode); } } catch (error) { console.error('Failed to load user preferences:', error); } }; // Save user preferences to database const saveUserPreferences = async (newDarkMode: boolean) => { if (!user) return; try { await fetch('/api/preferences', { method: 'PUT', headers: { 'Content-Type': 'application/json', }, credentials: 'include', body: JSON.stringify({ dark_mode: newDarkMode }) }); console.log('Saved user preferences to database'); } catch (error) { console.error('Failed to save user preferences:', error); } }; const checkAuthStatus = async () => { try { const response = await fetch('/auth/user', { credentials: 'include' }); if (response.ok) { const userData = await response.json(); setUser(userData.user); setAuthAvailable(true); } else if (response.status === 501) { // Authentication not configured setAuthAvailable(false); } else if (response.status === 401) { // Authentication configured but user not logged in setAuthAvailable(true); setUser(null); } else { // Other error setAuthAvailable(false); } } catch (error) { console.log('Auth check failed:', error); setAuthAvailable(false); } }; const handleLogin = () => { window.location.href = '/auth/login'; }; const handleLogout = async () => { try { await fetch('/auth/logout', { method: 'POST', credentials: 'include' }); setUser(null); // Optionally reload the page to reset any user-specific state window.location.reload(); } catch (error) { console.error('Logout failed:', error); } }; // Handle dark mode toggle with hybrid storage const handleDarkModeToggle = async () => { const newDarkMode = !darkMode; setDarkMode(newDarkMode); if (user) { // Save to database for authenticated users await saveUserPreferences(newDarkMode); } else { // Save to localStorage for non-authenticated users localStorage.setItem('darkMode', JSON.stringify(newDarkMode)); } }; useEffect(() => { // Apply dark mode if (darkMode) { document.documentElement.classList.add('dark'); } else { document.documentElement.classList.remove('dark'); } // Save to localStorage as backup (for non-authenticated users) if (!user) { localStorage.setItem('darkMode', JSON.stringify(darkMode)); } }, [darkMode, user]); // Helper function to format book names for display const formatBookName = (bookName: string): string => { // Remove leading numbers and underscores, replace underscores with spaces return bookName.replace(/^\d+_/, '').replace(/_/g, ' '); }; // Helper function to convert display name back to file name const getBookFileName = (displayName: string): string => { // Find the book that matches the display name const book = books.find((b: string) => formatBookName(b) === displayName); return book || displayName; }; // Helper function to convert book file name to URL-safe name const getBookUrlName = (bookName: string): string => { // Remove leading numbers and convert spaces to underscores for URL return bookName.replace(/^\d+_/, '').replace(/ /g, '_'); }; // Helper function to convert URL name back to file name const getBookFromUrl = (urlName: string): string => { // Convert URL name back to display name, then find the file name const displayName = urlName.replace(/_/g, ' '); return getBookFileName(displayName); }; const loadBooks = async () => { try { console.log('Loading books from API for version:', selectedVersion); const response = await fetch(`/books?version=${selectedVersion}`); const data: BookData = await response.json(); console.log('Books loaded:', data); setBooks(data.books); } catch (error) { console.error('Failed to load books:', error); setError('Failed to load books. Please check the console for details.'); } finally { setLoading(false); } }; const loadVersions = async () => { try { console.log('Loading available versions...'); const response = await fetch('/versions'); if (response.ok) { const data = await response.json(); setAvailableVersions(data.versions); console.log('Versions loaded:', data.versions); } else { console.error('Failed to load versions'); } } catch (error) { console.error('Failed to load versions:', error); } }; // Get current navigation info from URL const getCurrentNavInfo = () => { const pathParts = location.pathname.split('/').filter(Boolean); if (pathParts.length === 0) { return { currentBook: null, currentChapter: null }; } if (pathParts[0] === 'book' && pathParts[1]) { const currentBook = pathParts[1]; const currentChapter = pathParts[2] === 'chapter' && pathParts[3] ? pathParts[3] : null; return { currentBook, currentChapter }; } return { currentBook: null, currentChapter: null }; }; const { currentBook, currentChapter } = getCurrentNavInfo(); // Component for the home page const HomePage = () => { const handleBookSelect = (book: string) => { const urlName = getBookUrlName(book); navigate(`/book/${urlName}`); }; const handleFavoriteChange = () => { // This will trigger a re-render of the FavoritesMenu setUser((prev: any) => ({ ...prev })); }; return ( ); }; // Component for book chapters page const BookPage = () => { const { bookName } = useParams<{ bookName: string }>(); const actualBookName = bookName ? getBookFromUrl(bookName) : ''; const handleChapterSelect = (chapter: string) => { navigate(`/book/${bookName}/chapter/${chapter}`); }; const handleBack = () => { navigate('/'); }; const handleFavoriteChange = () => { // This will trigger a re-render of the FavoritesMenu setUser((prev: any) => ({ ...prev })); }; if (!bookName || !actualBookName || !books.includes(actualBookName)) { return
Book not found
; } return ( ); }; // Component for chapter reading page const ChapterPage = () => { const { bookName, chapterNumber } = useParams<{ bookName: string, chapterNumber: string }>(); const actualBookName = bookName ? getBookFromUrl(bookName) : ''; const handleBack = () => { navigate(`/book/${bookName}`); }; const handleFavoriteChange = () => { // This will trigger a re-render of the FavoritesMenu setUser((prev: any) => ({ ...prev })); }; if (!bookName || !chapterNumber || !actualBookName) { return
Chapter not found
; } return ( ); }; if (loading) { return (

Loading The Bible...

); } return (
{/* Header */}
{/* Version Selector */}
{/* Navigation Breadcrumb - Hidden on small screens */}
{currentBook && ( <> {formatBookName(currentBook)} {currentChapter && ( <> Chapter {currentChapter} )} )}
{/* Mobile Navigation - Simplified */}
{currentBook && ( <> {formatBookName(currentBook)} {currentChapter && ( <> Ch. {currentChapter} )} )}
{/* Search, User Authentication & Dark Mode */}
{/* Search Button */} {/* Authentication Button */} {authAvailable && (
{user ? (
{user.name || user.email}
) : ( )}
)} {/* Dark Mode Toggle */}
{/* Favorites Menu - Positioned below header for authenticated users */} {user && (
setUser((prev: any) => ({ ...prev }))} />
)} {/* Search Modal */} {showSearch && ( setShowSearch(false)} isModal={true} /> )} {/* Main Content */}
} /> } /> } /> } />
); } export default App;