import { Button } from '@/components/ui/button' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Switch } from '@/components/ui/switch' import managedOutboxService from '@/services/managed-outbox.service' import relayStatsService from '@/services/relay-stats.service' import storage, { dispatchSettingsChanged } from '@/services/local-storage.service' import type { TRelayEntry } from '@/types/relay-management' import type { TOutboxMode } from '@/types/relay-management' import { Check, ChevronDown, ChevronUp, Shield, ShieldAlert, ShieldOff, X } from 'lucide-react' import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' type RelayTab = 'pending' | 'approved' | 'rejected' function failureRateColor(rate: number): string { if (rate >= 0.99) return 'text-red-900 dark:text-red-300' if (rate > 0.5) return 'text-red-600 dark:text-red-400' if (rate > 0.1) return 'text-yellow-600 dark:text-yellow-400' return 'text-green-600 dark:text-green-400' } function RelayRow({ entry, onAction }: { entry: TRelayEntry; onAction: () => void }) { const { t } = useTranslation() const [expanded, setExpanded] = useState(false) const failureRate = relayStatsService.getFailureRate(entry.url) const autoDisabled = relayStatsService.isAutoDisabled(entry.url) return (
{entry.url} {autoDisabled && ( )} {entry.manualExclude && ( )}
{entry.direction} {(failureRate * 100).toFixed(0)}%
{expanded && (
{entry.reason && (
{entry.reason}
)} {entry.relayIp && (
IP: {entry.relayIp}
)}
{entry.status !== 'approved' && ( )} {entry.status !== 'rejected' && ( )} {entry.status !== 'pending' && ( )}
{ managedOutboxService.setManualExclude(entry.url, checked) onAction() }} /> {t('Exclude')}
)}
) } export default function ManagedOutboxSetting() { const { t } = useTranslation() const [outboxMode, setOutboxMode] = useState( storage.getOutboxMode() as TOutboxMode ) const [tab, setTab] = useState('pending') const [refreshKey, setRefreshKey] = useState(0) const refresh = useCallback(() => setRefreshKey((k) => k + 1), []) const pending = useMemo(() => managedOutboxService.getPendingRelays(), [refreshKey]) const approved = useMemo(() => managedOutboxService.getApprovedRelays(), [refreshKey]) const rejected = useMemo(() => managedOutboxService.getRejectedRelays(), [refreshKey]) const excluded = useMemo(() => managedOutboxService.getExcludedRelays(), [refreshKey]) const autoDisabled = useMemo(() => managedOutboxService.getAutoDisabledRelays(), [refreshKey]) const currentList = tab === 'pending' ? pending : tab === 'approved' ? approved : rejected const handleModeChange = (mode: string) => { storage.setOutboxMode(mode) setOutboxMode(mode as TOutboxMode) dispatchSettingsChanged() } return (

{t('Relays discovered via outbox model (NIP-65) are tracked here with per-network failure stats. In')} {' '}{t('Automatic')} {t('mode, all discovered relays are used unless manually excluded or auto-disabled. In')} {' '}{t('Managed')} {t('mode, relays must be explicitly approved before use.')}

{t('Approve/Reject controls whether a relay is used in managed mode. Exclude is a manual override that blocks a relay in both modes, independent of approval status. Relays with 99%+ failure rate on your current network are auto-disabled.')}

{t('Failure rates are tracked per network (based on your external IP), so a relay that fails on one connection may work fine on another.')}

{pending.length} {t('pending')} · {approved.length} {t('approved')} · {rejected.length} {t('rejected')} · {excluded.length} {t('excluded')} · {autoDisabled.length} {t('auto-disabled')}
{(['pending', 'approved', 'rejected'] as const).map((t_) => ( ))}
{tab === 'pending' && pending.length > 1 && (
)}
{currentList.length === 0 ? (
{t('No relays')}
) : ( currentList.map((entry) => ( )) )}
) }