import { useEffect, useRef, useState } from "preact/hooks"; import { WelcomeBarProps, WelcomeBarState } from "./welcome-types.ts"; import { useEncryption } from "./useEncryption.ts"; import { useLogoutCleanup, useServiceWorker } from "./useServiceWorker.ts"; import EncryptionModal from "./EncryptionModal.tsx"; import NavigationMenu from "./NavigationMenu.tsx"; import LockIcon from "./LockIcon.tsx"; /** * WelcomeBar Component * Main navigation bar with user info, encryption status, and menu * Refactored with consolidated state for V8 optimization * * Features: * - Consolidated state management (single state object) * - Modular component architecture * - Custom hooks for reusable logic * - Service worker management * - Encryption status display * * @param stateData - Session cookie data with user info * @param currentRoute - Current route path */ export default function WelcomeBar( { publicId, user_name, currentRoute, isEncrypted, stateId }: WelcomeBarProps, ) { // Use ref for menu button to avoid state-based re-renders const menuButtonRef = useRef(null); // Track current route dynamically (props only set on initial hydration) const [activeRoute, setActiveRoute] = useState(currentRoute); // Consolidated state for better V8 optimization const [state, setState] = useState>({ showPasswordModal: false, isPasswordSet: false, isSkipped: false, tempPassword: "", passwordError: "", showMenu: false, photoUrl: "", }); // Helper function for partial state updates const updateState = ( partial: Partial>, ) => { setState((prev) => ({ ...prev, ...partial })); }; // Function to manage localStorage stateId tracking const manageStateIdInStorage = () => { try { const storedStateId = localStorage.getItem("mystate"); if (storedStateId === null) { // No existing stateId, save the current one localStorage.setItem("mystate", stateId); } else if (storedStateId !== stateId) { // Existing stateId doesn't match, log it before saving //console.log(`StateId changed from ${storedStateId} to ${stateId}`); localStorage.setItem("mystate", stateId); } // If they match, no action needed } catch (error) { console.error("Error managing stateId in localStorage:", error); } }; // Custom hooks for modular logic //useServiceWorker(); useLogoutCleanup(); // Update activeRoute when browser location changes (client-side navigation) useEffect(() => { // Set initial route from globalThis.location if different from prop if ( typeof globalThis !== "undefined" && globalThis.location?.pathname !== activeRoute ) { setActiveRoute(globalThis.location.pathname); } // Listen for popstate (back/forward navigation) const handlePopState = () => { setActiveRoute(globalThis.location.pathname); }; // Listen for custom navigation events (if using client-side routing) const handleNavigation = () => { setActiveRoute(globalThis.location.pathname); }; globalThis.addEventListener("popstate", handlePopState); // Also check on click events that might trigger navigation document.addEventListener("click", handleNavigation); return () => { globalThis.removeEventListener("popstate", handlePopState); document.removeEventListener("click", handleNavigation); }; }, []); // useEffect to run localStorage management only when stateId changes useEffect(() => { manageStateIdInStorage(); }, [stateId]); // Only use encryption hook if user is logged in (has valid user_id) const hasValidUserId = publicId && publicId.length > 0; const encryption = useEncryption( hasValidUserId ? publicId : "", isEncrypted, ); // Menu handlers with error boundaries const handleMenuToggle = (event?: Event) => { try { // Prevent event bubbling to avoid triggering click outside handlers if (event && typeof event.stopPropagation === "function") { event.stopPropagation(); } updateState({ showMenu: !state.showMenu }); } catch (error) { console.error("Error toggling menu:", error); } }; const handleMenuClose = () => { try { updateState({ showMenu: false }); } catch (error) { console.error("Error closing menu:", error); } }; // Allow rendering even for guest users - don't block on missing stateData // If stateData is completely missing, create a safe default for guest state return ( <>
{user_name ? (
{state.photoUrl ? ( User profile { // Fallback to initials if image fails to load (e.target as HTMLImageElement).style.display = "none"; const fallback = document.getElementById( `initials-${user_name || "default"}`, ); if (fallback) fallback.style.display = "flex"; }} /> ) : null}
{user_name.substring(0, 2).toUpperCase()}
) : (
)}
{/* Lock icons showing encryption status - only for logged in users */} {hasValidUserId && (
)} {/* Logo positioned by percentage across the bar */} {activeRoute !== "/" && ( )}
{/* Hamburger Menu - always show */} {/* Navigation Menu Component - positioned relative to button */}
{/* Encryption Modal Component - only for logged in users */} {hasValidUserId && encryption.showPasswordModal && ( )} {/* Change PIN Modal Component - only for logged in users */} {hasValidUserId && ( )} ); }