import { useMemo, type ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import { Popover, Switch, Tag, type TableColumnType } from 'antd'; import { TeamOutlined } from '@ant-design/icons'; import { SizeFormatter, IntlUtil, ColorUtils } from '@/utils'; import { InfinityIcon } from '@/components/ui'; import { useDatepicker } from '@/hooks/useDatepicker'; import type { NodeRecord } from '@/api/queries/useNodesQuery'; import { RowActionsCell } from './RowActions'; import { readStreamHints, networkLabel, networkL4, shadowsocksNetworkLabel, tunnelNetworkLabel, mixedNetworkLabel, } from './helpers'; import type { ClientCountEntry, DBInboundRecord, RowAction } from './types'; interface UseInboundColumnsParams { hasAnyRemark: boolean; hasActiveNode: boolean; nodesById: Map; clientCount: Record; subEnable: boolean; expireDiff: number; trafficDiff: number; onRowAction: (action: { key: RowAction; dbInbound: DBInboundRecord }) => void; onSwitchEnable: (dbInbound: DBInboundRecord, next: boolean) => void; } export function useInboundColumns({ hasAnyRemark, hasActiveNode, nodesById, clientCount, subEnable, expireDiff, trafficDiff, onRowAction, onSwitchEnable, }: UseInboundColumnsParams): TableColumnType[] { const { t } = useTranslation(); const { datepicker } = useDatepicker(); return useMemo(() => { const cols: TableColumnType[] = [ { title: 'ID', dataIndex: 'id', key: 'id', align: 'right', width: 30, }, { title: t('pages.inbounds.operate'), key: 'action', align: 'center', width: 60, render: (_, record) => ( 0} onClick={(key) => onRowAction({ key, dbInbound: record })} /> ), }, { title: t('pages.inbounds.enable'), key: 'enable', align: 'center', width: 35, render: (_, record) => ( onSwitchEnable(record, next)} /> ), }, ]; if (hasAnyRemark) { cols.push({ title: t('pages.inbounds.remark'), dataIndex: 'remark', key: 'remark', align: 'center', width: 60, }); } if (hasActiveNode) { cols.push({ title: t('pages.inbounds.node'), key: 'node', align: 'center', width: 60, render: (_, record) => { if (record.nodeId == null) { return {t('pages.inbounds.localPanel')}; } const node = nodesById.get(record.nodeId); if (!node) { return node #{record.nodeId}; } return ( {node.name} ); }, }); } cols.push( { title: t('pages.inbounds.port'), dataIndex: 'port', key: 'port', align: 'center', width: 40, }, { title: t('pages.inbounds.protocol'), key: 'protocol', align: 'left', width: 130, render: (_, record) => { const tags: ReactElement[] = [{record.protocol}]; if (record.isWireguard || record.isHysteria) { tags.push(UDP); } else if (record.isSS) { const stream = readStreamHints(record.streamSettings); tags.push({shadowsocksNetworkLabel(record.settings)}); if (stream.isTls) tags.push(TLS); } else if (record.isTunnel) { tags.push({tunnelNetworkLabel(record.settings)}); } else if (record.isMixed) { tags.push({mixedNetworkLabel(record.settings)}); } else if (record.isVMess || record.isVLess || record.isTrojan) { const stream = readStreamHints(record.streamSettings); tags.push({networkLabel(stream.network)}); const l4 = networkL4(stream.network); if (l4) tags.push({l4}); if (stream.isTls) tags.push(TLS); if (stream.isReality) tags.push(Reality); } return
{tags}
; }, }, { title: t('clients'), key: 'clients', align: 'left', width: 110, render: (_, record) => { const cc = clientCount[record.id]; if (!cc) return null; return ( <> {cc.clients} {cc.active.length > 0 && ( {cc.active.map((e) =>
{e}
)} )} > {cc.active.length}
)} {cc.deactive.length > 0 && ( {cc.deactive.map((e) =>
{e}
)} )} > {cc.deactive.length}
)} {cc.depleted.length > 0 && ( {cc.depleted.map((e) =>
{e}
)} )} > {cc.depleted.length}
)} {cc.online.length > 0 && ( {cc.online.map((e) =>
{e}
)} )} > {cc.online.length}
)} ); }, }, { title: t('pages.inbounds.traffic'), key: 'traffic', align: 'center', width: 90, render: (_, record) => ( ↑ {SizeFormatter.sizeFormat(record.up)} ↓ {SizeFormatter.sizeFormat(record.down)} {record.total > 0 && record.up + record.down < record.total && ( {t('remained')} {SizeFormatter.sizeFormat(record.total - record.up - record.down)} )} )} > {SizeFormatter.sizeFormat(record.up + record.down)} / {' '} {record.total > 0 ? SizeFormatter.sizeFormat(record.total) : } ), }, { title: t('pages.inbounds.expireDate'), key: 'expiryTime', align: 'center', width: 40, render: (_, record) => { if (record.expiryTime > 0) { return ( {IntlUtil.formatRelativeTime(record.expiryTime)} ); } return ; }, }, ); return cols; }, [t, hasAnyRemark, hasActiveNode, nodesById, clientCount, subEnable, expireDiff, trafficDiff, datepicker, onRowAction, onSwitchEnable]); }