import cx from 'clsx'; import { formatDistanceToNow } from 'date-fns'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { RuleExtra } from '~/api/rules'; import { Activity, FileText, Globe, Hash, Link, Shield, Zap } from '~/components/shared/FeatherIcons'; import SwitchThemed from '~/components/SwitchThemed'; import { useToggleRuleDisabled } from '~/modules/rules/hooks'; import { ClashAPIConfig } from '~/types'; import s0 from './Rule.module.scss'; const colorMap = { _default: 'var(--color-focus-blue)', DIRECT: '#f5bc41', REJECT: '#cb3166', }; function getStyleFor({ proxy }) { let color = colorMap._default; if (colorMap[proxy]) { color = colorMap[proxy]; } return { color }; } function getIconFor(type: string) { switch (type) { case 'Domain': case 'DomainSuffix': case 'DomainKeyword': return ; case 'IPCIDR': case 'IPCIDR6': return ; case 'GeoSite': case 'GeoIP': return ; case 'REJECT': return ; case 'DIRECT': return ; default: return ; } } type RuleProviderLookup = { byName: Record; }; type Props = { id?: number; type?: string; payload?: string; proxy?: string; size?: number; extra?: RuleExtra; apiConfig?: ClashAPIConfig; provider?: RuleProviderLookup; }; function getEntryCount({ type, payload, size, provider, }: { type: string; payload: string; size: number; provider?: RuleProviderLookup; }): number | undefined { if ((type === 'GeoSite' || type === 'GeoIP') && size >= 0) { return size; } if (type === 'RuleSet') { return provider?.byName?.[payload]?.ruleCount; } return undefined; } function Rule({ type, payload, proxy, id, size, extra, apiConfig, provider }: Props) { const { t } = useTranslation(); const styleProxy = getStyleFor({ proxy }); const { toggleRule, isPending } = useToggleRuleDisabled(apiConfig); const disabled = extra?.disabled ?? false; const entryCount = getEntryCount({ type, payload, size, provider }); const hitTitle = extra ? extra.hitCount > 0 ? t('rule_hit_tip', { count: extra.hitCount, time: formatDistanceToNow(new Date(extra.hitAt), { addSuffix: true }), }) : t('rule_never_hit') : undefined; return ( {id} {payload} {typeof entryCount === 'number' && ( {t('rule_entry_count', { count: entryCount })} )} {getIconFor(type)} {type} {proxy} {extra && ( {extra.hitCount} )} {extra && ( toggleRule(id, !checked)} /> )} ); } export default Rule;