import { useCallback, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { FaDownload } from "react-icons/fa"; import { FiWifi } from "react-icons/fi"; import { GoGear, GoKebabHorizontal, GoPlus } from "react-icons/go"; import { LuCloud, LuPlug, LuPuzzle, LuSearch, LuUsers, LuX, } from "react-icons/lu"; import { cn } from "@/lib/utils"; import { Logo } from "./icons/logo"; import { Button } from "./ui/button"; import { CardTitle } from "./ui/card"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "./ui/dropdown-menu"; import { Input } from "./ui/input"; import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; const CLICK_THRESHOLD = 5; const CLICK_WINDOW_MS = 2000; const GRAVITY = 2200; const BOUNCE_DAMPING = 0.6; const INITIAL_HORIZONTAL_SPEED = 350; const SPIN_SPEED = 720; const MIN_BOUNCE_VELOCITY = 60; const LOGO_HIDDEN_KEY = "donut-logo-hidden"; function useLogoEasterEgg() { const clickTimestamps = useRef([]); const [isPressed, setIsPressed] = useState(false); const [wobbleKey, setWobbleKey] = useState(0); const [isFalling, setIsFalling] = useState(false); const [isHidden, setIsHidden] = useState(() => { try { return sessionStorage.getItem(LOGO_HIDDEN_KEY) === "1"; } catch { return false; } }); const logoRef = useRef(null); const animFrameRef = useRef(0); const triggerFall = useCallback(() => { const el = logoRef.current; if (!el || isFalling) return; setIsFalling(true); const rect = el.getBoundingClientRect(); const startX = rect.left; const startY = rect.top; const floorY = window.innerHeight; const leftWall = 0; const rightWall = window.innerWidth; const clone = el.cloneNode(true) as HTMLElement; clone.style.position = "fixed"; clone.style.left = `${startX}px`; clone.style.top = `${startY}px`; clone.style.zIndex = "9999"; clone.style.pointerEvents = "none"; clone.style.margin = "0"; document.body.appendChild(clone); el.style.visibility = "hidden"; let x = 0; let y = 0; let vy = -500; let vx = -INITIAL_HORIZONTAL_SPEED; let rotation = 0; let lastTime = performance.now(); const animate = (time: number) => { const dt = Math.min((time - lastTime) / 1000, 0.05); lastTime = time; vy += GRAVITY * dt; x += vx * dt; y += vy * dt; rotation += SPIN_SPEED * dt * (vx > 0 ? 1 : -1); // Floor bounce const currentBottom = startY + y + rect.height; if (currentBottom >= floorY && vy > 0) { y = floorY - startY - rect.height; if (Math.abs(vy) > MIN_BOUNCE_VELOCITY) { vy = -Math.abs(vy) * BOUNCE_DAMPING; } else { vy = -MIN_BOUNCE_VELOCITY * 3; } } // Left wall bounce only — right wall lets it fly off screen const currentLeft = startX + x; if (currentLeft <= leftWall && vx < 0) { x = leftWall - startX; vx = Math.abs(vx) * 1.1; } clone.style.transform = `translate(${x}px, ${y}px) rotate(${rotation}deg)`; // Only end when fully off-screen vertically (bounced out the top or flew off bottom somehow) const currentTop = startY + y; const offScreenRight = startX + x > rightWall + 50; const offScreenBottom = currentTop > floorY + 100; const offScreenTop = currentTop + rect.height < -200; if (offScreenRight || offScreenBottom || offScreenTop) { clone.remove(); try { sessionStorage.setItem(LOGO_HIDDEN_KEY, "1"); } catch { // ignore } setIsHidden(true); setIsFalling(false); return; } animFrameRef.current = requestAnimationFrame(animate); }; animFrameRef.current = requestAnimationFrame(animate); }, [isFalling]); useEffect(() => { return () => { if (animFrameRef.current) cancelAnimationFrame(animFrameRef.current); }; }, []); const handleClick = useCallback(() => { if (isFalling || isHidden) return; const now = Date.now(); clickTimestamps.current = clickTimestamps.current.filter( (t) => now - t < CLICK_WINDOW_MS, ); clickTimestamps.current.push(now); if (clickTimestamps.current.length >= CLICK_THRESHOLD) { clickTimestamps.current = []; triggerFall(); } else { setWobbleKey((k) => k + 1); } }, [isFalling, isHidden, triggerFall]); return { logoRef, isPressed, setIsPressed, wobbleKey, isFalling, isHidden, handleClick, }; } interface Props { onSettingsDialogOpen: (open: boolean) => void; onProxyManagementDialogOpen: (open: boolean) => void; onGroupManagementDialogOpen: (open: boolean) => void; onImportProfileDialogOpen: (open: boolean) => void; onCreateProfileDialogOpen: (open: boolean) => void; onSyncConfigDialogOpen: (open: boolean) => void; onIntegrationsDialogOpen: (open: boolean) => void; onExtensionManagementDialogOpen: (open: boolean) => void; searchQuery: string; onSearchQueryChange: (query: string) => void; } const HomeHeader = ({ onSettingsDialogOpen, onProxyManagementDialogOpen, onGroupManagementDialogOpen, onImportProfileDialogOpen, onCreateProfileDialogOpen, onSyncConfigDialogOpen, onIntegrationsDialogOpen, onExtensionManagementDialogOpen, searchQuery, onSearchQueryChange, }: Props) => { const { t } = useTranslation(); const { logoRef, isPressed, setIsPressed, wobbleKey, isFalling, isHidden, handleClick, } = useLogoEasterEgg(); return (
{!isHidden ? ( ) : (
)} Donut
{ onSearchQueryChange(e.target.value); }} className="pr-8 pl-10 w-48" /> {searchQuery && ( )}
{t("header.moreActions")} { onSettingsDialogOpen(true); }} > {t("header.menu.settings")} { onProxyManagementDialogOpen(true); }} > {t("header.menu.proxies")} { onGroupManagementDialogOpen(true); }} > {t("header.menu.groups")} { onExtensionManagementDialogOpen(true); }} > {t("header.menu.extensions")} { onSyncConfigDialogOpen(true); }} > {t("header.menu.syncService")} { onIntegrationsDialogOpen(true); }} > {t("header.menu.integrations")} { onImportProfileDialogOpen(true); }} > {t("header.menu.importProfile")} {t("header.createProfile")}
); }; export default HomeHeader;