import { Link, useLocation } from 'react-router-dom'; import { useCallback, useEffect, useState } from 'react'; import { siDiscord } from 'simple-icons'; import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { FolderOpen, Settings, BookOpen, MessageCircleQuestion, MessageCircle, Menu, Plus, } from 'lucide-react'; import { Logo } from '@/components/logo'; import { SearchBar } from '@/components/search-bar'; import { useSearch } from '@/contexts/search-context'; import { openTaskForm } from '@/lib/openTaskForm'; import { useProject } from '@/contexts/project-context'; import { showProjectForm } from '@/lib/modals'; import { useOpenProjectInEditor } from '@/hooks/useOpenProjectInEditor'; const DISCORD_GUILD_ID = '1423630976524877857'; const INTERNAL_NAV = [ { label: 'Projects', icon: FolderOpen, to: '/projects' }, { label: 'Settings', icon: Settings, to: '/settings' }, ]; const EXTERNAL_LINKS = [ { label: 'Docs', icon: BookOpen, href: 'https://vibekanban.com/docs', }, { label: 'Support', icon: MessageCircleQuestion, href: 'https://github.com/BloopAI/vibe-kanban/issues', }, { label: 'Discord', icon: MessageCircle, href: 'https://discord.gg/AC4nwVtJM3', }, ]; export function Navbar() { const location = useLocation(); const { projectId, project } = useProject(); const { query, setQuery, active, clear, registerInputRef } = useSearch(); const handleOpenInEditor = useOpenProjectInEditor(project || null); const [onlineCount, setOnlineCount] = useState(null); useEffect(() => { let cancelled = false; const fetchCount = async () => { try { const res = await fetch( `https://discord.com/api/guilds/${DISCORD_GUILD_ID}/widget.json`, { cache: 'no-store' } ); if (!res.ok) return; // Widget disabled or temporary error; keep previous value const data = await res.json(); if (!cancelled && typeof data?.presence_count === 'number') { setOnlineCount(data.presence_count); } } catch { // Network error; ignore and keep previous value } }; // Initial fetch + refresh every 60s fetchCount(); const interval = setInterval(fetchCount, 60_000); return () => { cancelled = true; clearInterval(interval); }; }, []); const setSearchBarRef = useCallback( (node: HTMLInputElement | null) => { registerInputRef(node); }, [registerInputRef] ); const handleCreateTask = () => { if (projectId) { openTaskForm({ projectId }); } }; const handleOpenInIDE = () => { handleOpenInEditor(); }; const handleProjectSettings = async () => { try { await showProjectForm({ project }); // Settings saved successfully - no additional action needed } catch (error) { // User cancelled - do nothing } }; return (
{onlineCount !== null ? `${onlineCount.toLocaleString()} online` : 'online'}
{projectId && ( <> )} {INTERNAL_NAV.map((item) => { const active = location.pathname.startsWith(item.to); const Icon = item.icon; return ( {item.label} ); })} {EXTERNAL_LINKS.map((item) => { const Icon = item.icon; return ( {item.label} ); })}
); }