import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Button, Popover, Table, Tag, Tooltip } from 'antd'; import { ThunderboltOutlined, CheckCircleFilled, CloseCircleFilled, LoadingOutlined, } from '@ant-design/icons'; import type { ColumnsType } from 'antd/es/table'; import { SizeFormatter } from '@/utils'; import { OutboundProtocols as Protocols } from '@/schemas/primitives'; import { isUdpOutbound } from '@/hooks/useXraySetting'; import type { OutboundTestState, OutboundTrafficRow } from '@/hooks/useXraySetting'; import type { OutboundRow } from './outbounds-tab-types'; import { hasBreakdown, isTesting, isUntestable, outboundAddresses, showSecurity, testResult, trafficFor, } from './outbounds-tab-helpers'; interface SubscriptionOutboundsProps { subscriptionOutbounds: unknown[]; outboundsTraffic: OutboundTrafficRow[]; subscriptionTestStates: Record; testMode: 'tcp' | 'http'; isMobile: boolean; onTestSubscription: (outbound: Record, mode: string) => void; } // Read-only view of outbounds imported from active subscriptions. They are not // part of the editable template (so no edit/delete/move), but traffic is matched // by tag and they can be latency-tested via the same backend endpoint. export default function SubscriptionOutbounds({ subscriptionOutbounds, outboundsTraffic, subscriptionTestStates, testMode, isMobile, onTestSubscription, }: SubscriptionOutboundsProps) { const { t } = useTranslation(); const rows = useMemo( () => (subscriptionOutbounds || []).map((o, i) => ({ ...(o as object), key: i }) as OutboundRow), [subscriptionOutbounds], ); if (rows.length === 0) return null; const identityCell = (record: OutboundRow) => (
{record.tag || '—'}
{record.protocol} {[Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(record.protocol as never) && ( <> {record.streamSettings?.network} {showSecurity(record.streamSettings?.security) && {record.streamSettings?.security}} )}
); const addressCell = (record: OutboundRow) => { const addrs = outboundAddresses(record); return (
{addrs.length === 0 ? ( ) : ( addrs.map((addr) => ( {addr} )) )}
); }; const trafficCell = (record: OutboundRow) => { const tr = trafficFor(outboundsTraffic, record); return ( <> ↑ {SizeFormatter.sizeFormat(tr.up)} ↓ {SizeFormatter.sizeFormat(tr.down)} ); }; const latencyCell = (record: OutboundRow) => { const key = record.tag || ''; const r = testResult(subscriptionTestStates, key); if (!r) return isTesting(subscriptionTestStates, key) ? : ; return (
{r.success ? {r.delay} ms : {r.error || 'failed'}} {r.mode && {String(r.mode).toUpperCase()}}
{hasBreakdown(r) && ( <> {(r.endpoints || []).map((ep) => (
{ep.address} {ep.success ? `${ep.delay} ms` : ep.error || 'failed'}
))} )} } > {r.success ? : } {r.success ? {r.delay} ms : failed}
); }; const testButton = (record: OutboundRow) => { const key = record.tag || ''; return (