"use client"; import { useState, useEffect, useRef } from "react"; import PropTypes from "prop-types"; import { Badge, Toggle } from "@/shared/components"; import CooldownTimer from "./CooldownTimer"; export default function ConnectionRow({ connection, proxyPools, isOAuth, isFirst, isLast, onMoveUp, onMoveDown, onToggleActive, onUpdateProxy, onEdit, onDelete }) { const [showProxyDropdown, setShowProxyDropdown] = useState(false); const [updatingProxy, setUpdatingProxy] = useState(false); const proxyDropdownRef = useRef(null); const proxyPoolMap = new Map((proxyPools || []).map((pool) => [pool.id, pool])); const boundProxyPoolId = connection.providerSpecificData?.proxyPoolId || null; const boundProxyPool = boundProxyPoolId ? proxyPoolMap.get(boundProxyPoolId) : null; const hasLegacyProxy = connection.providerSpecificData?.connectionProxyEnabled === true && !!connection.providerSpecificData?.connectionProxyUrl; const hasAnyProxy = !!boundProxyPoolId || hasLegacyProxy; const proxyDisplayText = boundProxyPool ? `Pool: ${boundProxyPool.name}` : boundProxyPoolId ? `Pool: ${boundProxyPoolId} (inactive/missing)` : hasLegacyProxy ? `Legacy: ${connection.providerSpecificData?.connectionProxyUrl}` : ""; let maskedProxyUrl = ""; if (boundProxyPool?.proxyUrl || connection.providerSpecificData?.connectionProxyUrl) { const rawProxyUrl = boundProxyPool?.proxyUrl || connection.providerSpecificData?.connectionProxyUrl; try { const parsed = new URL(rawProxyUrl); maskedProxyUrl = `${parsed.protocol}//${parsed.hostname}${parsed.port ? `:${parsed.port}` : ""}`; } catch { maskedProxyUrl = rawProxyUrl; } } const noProxyText = boundProxyPool?.noProxy || connection.providerSpecificData?.connectionNoProxy || ""; let proxyBadgeVariant = "default"; if (boundProxyPool?.isActive === true) { proxyBadgeVariant = "success"; } else if (boundProxyPoolId || hasLegacyProxy) { proxyBadgeVariant = "error"; } // Close dropdown when clicking outside useEffect(() => { if (!showProxyDropdown) return; const handler = (e) => { if (proxyDropdownRef.current && !proxyDropdownRef.current.contains(e.target)) { setShowProxyDropdown(false); } }; document.addEventListener("mousedown", handler); return () => document.removeEventListener("mousedown", handler); }, [showProxyDropdown]); const handleSelectProxy = async (poolId) => { setUpdatingProxy(true); try { await onUpdateProxy(poolId === "__none__" ? null : poolId); } finally { setUpdatingProxy(false); setShowProxyDropdown(false); } }; const isEmail = (v) => typeof v === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v); const displayName = isOAuth ? (isEmail(connection.email) ? connection.email : (isEmail(connection.name) ? connection.name : (connection.name || connection.email || connection.displayName || "OAuth Account"))) : connection.name; // Use useState + useEffect for impure Date.now() to avoid calling during render const [isCooldown, setIsCooldown] = useState(false); // Get earliest model lock timestamp (useEffect handles the Date.now() comparison) const modelLockUntil = Object.entries(connection) .filter(([k]) => k.startsWith("modelLock_")) .map(([, v]) => v) .filter(v => !!v) .sort()[0] || null; useEffect(() => { const checkCooldown = () => { const until = Object.entries(connection) .filter(([k]) => k.startsWith("modelLock_")) .map(([, v]) => v) .filter(v => v && new Date(v).getTime() > Date.now()) .sort()[0] || null; setIsCooldown(!!until); }; checkCooldown(); const interval = modelLockUntil ? setInterval(checkCooldown, 1000) : null; return () => { if (interval) clearInterval(interval); }; }, [modelLockUntil]); // Determine effective status (override unavailable if cooldown expired) const effectiveStatus = (connection.testStatus === "unavailable" && !isCooldown) ? "active" // Cooldown expired u2192 treat as active : connection.testStatus; const getStatusVariant = () => { if (connection.isActive === false) return "default"; if (effectiveStatus === "active" || effectiveStatus === "success") return "success"; if (effectiveStatus === "error" || effectiveStatus === "expired" || effectiveStatus === "unavailable") return "error"; return "default"; }; return (
{displayName}
{maskedProxyUrl}
)}
{noProxyText && (
no_proxy: {noProxyText}
)}