import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Popover, Progress } from 'antd'; import InfinityIcon from '@/components/ui/InfinityIcon'; import { useTheme } from '@/hooks/useTheme'; import { computeTrafficDisplay } from '@/lib/clients/traffic-display'; import { SizeFormatter } from '@/utils'; import './ClientTrafficCell.css'; export interface ClientTrafficCellProps { up?: number; down?: number; total?: number; enabled?: boolean; trafficDiff?: number; compact?: boolean; } // Every prop is a primitive and the component is pure, so the memo bails out // whenever a client's counters did not move — which is most of them on most // pushes. Each skipped instance is one antd Popover (rc-trigger), one Progress, // a useTranslation subscription and a theme context read, times up to 200 rows. const ClientTrafficCell = memo(function ClientTrafficCell({ up = 0, down = 0, total = 0, enabled = true, trafficDiff = 0, compact = false, }: ClientTrafficCellProps) { const { t } = useTranslation(); const { isDark } = useTheme(); const display = useMemo( () => computeTrafficDisplay({ up, down, total, enabled, trafficDiff }, isDark), [up, down, total, enabled, trafficDiff, isDark], ); const popover = ( {!display.isUnlimited && ( )}
{SizeFormatter.sizeFormat(up)} {SizeFormatter.sizeFormat(down)}
{t('remained')} {SizeFormatter.sizeFormat(display.remaining)}
); const rootClass = [ 'client-traffic-cell', compact ? 'is-compact' : '', display.isUnlimited ? 'is-unlimited' : '', ] .filter(Boolean) .join(' '); return (
{SizeFormatter.sizeFormat(display.used)} {display.isUnlimited ? ( ) : ( SizeFormatter.sizeFormat(total) )}
); }); export default ClientTrafficCell;