import type { ServerProfile } from '../store/authStoreTypes'; import React, { useState, useEffect, useLayoutEffect, useRef, useCallback } from 'react'; import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { Check, ChevronDown } from 'lucide-react'; import { ConnectionStatus } from '../hooks/useConnectionStatus'; import { useAuthStore } from '../store/authStore'; import { switchActiveServer } from '../utils/server/switchActiveServer'; import { showToast } from '../utils/ui/toast'; import { serverListDisplayLabel } from '../utils/server/serverDisplayName'; interface Props { status: ConnectionStatus; isLan: boolean; serverName: string; } export default function ConnectionIndicator({ status, isLan, serverName }: Props) { const { t } = useTranslation(); const navigate = useNavigate(); const servers = useAuthStore(s => s.servers); const activeServerId = useAuthStore(s => s.activeServerId); const [menuOpen, setMenuOpen] = useState(false); const [switchingId, setSwitchingId] = useState(null); const [menuFixed, setMenuFixed] = useState({ top: 0, right: 0 }); const hostRef = useRef(null); const menuPanelRef = useRef(null); const multi = servers.length > 1; const updateMenuPosition = useCallback(() => { const el = hostRef.current; if (!el) return; const r = el.getBoundingClientRect(); setMenuFixed({ top: r.bottom + 6, right: window.innerWidth - r.right }); }, []); useLayoutEffect(() => { if (!menuOpen) return; updateMenuPosition(); const onWin = () => updateMenuPosition(); window.addEventListener('resize', onWin); window.addEventListener('scroll', onWin, true); return () => { window.removeEventListener('resize', onWin); window.removeEventListener('scroll', onWin, true); }; }, [menuOpen, updateMenuPosition]); useEffect(() => { if (!menuOpen) return; const onDown = (e: MouseEvent) => { const t = e.target as Node; if (hostRef.current?.contains(t)) return; if (menuPanelRef.current?.contains(t)) return; setMenuOpen(false); }; const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setMenuOpen(false); }; document.addEventListener('mousedown', onDown); document.addEventListener('keydown', onKey); return () => { document.removeEventListener('mousedown', onDown); document.removeEventListener('keydown', onKey); }; }, [menuOpen]); const goServerSettings = () => { setMenuOpen(false); navigate('/settings', { state: { tab: 'servers' } }); }; const onTriggerClick = () => { if (!multi) { goServerSettings(); return; } setMenuOpen(o => !o); }; const onPickServer = async (srv: ServerProfile) => { if (srv.id === activeServerId) { setMenuOpen(false); return; } setSwitchingId(srv.id); const ok = await switchActiveServer(srv); setSwitchingId(null); setMenuOpen(false); if (!ok) { showToast(t('connection.switchFailed'), 5000, 'error'); return; } navigate('/'); }; const label = isLan ? 'LAN' : t('connection.extern'); const tooltip = multi ? t('connection.switchServerHint') : status === 'connected' ? t('connection.connectedTo', { server: serverName }) : status === 'disconnected' ? t('connection.disconnectedFrom', { server: serverName }) : t('connection.checking'); return (
{label} {serverName} {multi && ( )}
{multi && menuOpen && typeof document !== 'undefined' && createPortal(
{t('connection.switchServerTitle')}
{servers.map(srv => { const active = srv.id === activeServerId; const busy = switchingId !== null; const labelText = serverListDisplayLabel(srv, servers); return ( ); })}
, document.body )}
); }