import React from 'react'; import { RiDownloadLine, RiInformationLine, RiQuestionLine, RiSettings3Line } from '@remixicon/react'; import { toast } from '@/components/ui'; import { cn } from '@/lib/utils'; import { ErrorBoundary } from '../ui/ErrorBoundary'; import { useUIStore } from '@/stores/useUIStore'; import { useUpdateStore } from '@/stores/useUpdateStore'; import { UpdateDialog } from '../ui/UpdateDialog'; import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip'; export const SIDEBAR_CONTENT_WIDTH = 264; const SIDEBAR_MIN_WIDTH = 200; const SIDEBAR_MAX_WIDTH = 500; const CHECK_FOR_UPDATES_EVENT = 'openchamber:check-for-updates'; interface SidebarProps { isOpen: boolean; isMobile: boolean; children: React.ReactNode; } export const Sidebar: React.FC = ({ isOpen, isMobile, children }) => { const { sidebarWidth, setSidebarWidth, setSettingsDialogOpen, setAboutDialogOpen, toggleHelpDialog } = useUIStore(); const [isResizing, setIsResizing] = React.useState(false); const startXRef = React.useRef(0); const startWidthRef = React.useRef(sidebarWidth || SIDEBAR_CONTENT_WIDTH); const [updateDialogOpen, setUpdateDialogOpen] = React.useState(false); const updateStore = useUpdateStore(); const pendingMenuUpdateCheckRef = React.useRef(false); const checkForUpdates = updateStore.checkForUpdates; const { available, downloaded, checking } = updateStore; const [isDesktopApp, setIsDesktopApp] = React.useState(() => { if (typeof window === 'undefined') { return false; } return Boolean((window as unknown as { __TAURI__?: unknown }).__TAURI__); }); React.useEffect(() => { if (typeof window === 'undefined') { return; } setIsDesktopApp(Boolean((window as unknown as { __TAURI__?: unknown }).__TAURI__)); }, []); React.useEffect(() => { if (typeof window === 'undefined') { return; } const handleMenuUpdateCheck = () => { if (!(window as unknown as { __TAURI__?: unknown }).__TAURI__) { return; } pendingMenuUpdateCheckRef.current = true; void checkForUpdates(); }; window.addEventListener(CHECK_FOR_UPDATES_EVENT, handleMenuUpdateCheck as EventListener); return () => { window.removeEventListener(CHECK_FOR_UPDATES_EVENT, handleMenuUpdateCheck as EventListener); }; }, [checkForUpdates]); React.useEffect(() => { if (!pendingMenuUpdateCheckRef.current) { return; } if (checking) { return; } if (available || downloaded) { setUpdateDialogOpen(true); } else { toast.success('No updates available', { description: 'You are running the latest version.', }); } pendingMenuUpdateCheckRef.current = false; }, [available, downloaded, checking]); React.useEffect(() => { if (isMobile || !isResizing) { return; } const handlePointerMove = (event: PointerEvent) => { const delta = event.clientX - startXRef.current; const nextWidth = Math.min( SIDEBAR_MAX_WIDTH, Math.max(SIDEBAR_MIN_WIDTH, startWidthRef.current + delta) ); setSidebarWidth(nextWidth); }; const handlePointerUp = () => { setIsResizing(false); }; window.addEventListener('pointermove', handlePointerMove); window.addEventListener('pointerup', handlePointerUp, { once: true }); return () => { window.removeEventListener('pointermove', handlePointerMove); window.removeEventListener('pointerup', handlePointerUp); }; }, [isMobile, isResizing, setSidebarWidth]); React.useEffect(() => { if (isMobile && isResizing) { setIsResizing(false); } }, [isMobile, isResizing]); if (isMobile) { return null; } const appliedWidth = isOpen ? Math.min( SIDEBAR_MAX_WIDTH, Math.max(SIDEBAR_MIN_WIDTH, sidebarWidth || SIDEBAR_CONTENT_WIDTH) ) : 0; const handlePointerDown = (event: React.PointerEvent) => { if (!isOpen) { return; } setIsResizing(true); startXRef.current = event.clientX; startWidthRef.current = appliedWidth; event.preventDefault(); }; return ( ); };