/** * Cache Relays Setting Component * * Configure NRC connections as cache relays for faster event loading. * Cache relays are queried first (400ms timeout) before regular relays, * and loaded events are pushed to them in background. */ import { useState, useCallback } from 'react' import { useTranslation } from 'react-i18next' import { useNostr } from '@/providers/NostrProvider' import nrcCacheRelayService, { NRCCacheRelayConfig } from '@/services/nrc/nrc-cache-relay.service' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Switch } from '@/components/ui/switch' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog' import { Plus, Trash2, Zap, Database } from 'lucide-react' export default function CacheRelaysSetting() { const { t } = useTranslation() const { pubkey } = useNostr() // Cache relay state const [cacheRelays, setCacheRelays] = useState(nrcCacheRelayService.getAll()) const [isDialogOpen, setIsDialogOpen] = useState(false) const [uri, setUri] = useState('') const [label, setLabel] = useState('') const [queryFirst, setQueryFirst] = useState(true) const [pushEvents, setPushEvents] = useState(true) const [isLoading, setIsLoading] = useState(false) const [isTesting, setIsTesting] = useState(null) const [error, setError] = useState(null) const handleAdd = useCallback(async () => { if (!uri.trim() || !label.trim()) return setIsLoading(true) setError(null) try { // Test connection first await nrcCacheRelayService.testConnection(uri.trim()) // Add the cache relay nrcCacheRelayService.add({ uri: uri.trim(), label: label.trim(), enabled: true, queryFirst, pushEvents }) setCacheRelays(nrcCacheRelayService.getAll()) setIsDialogOpen(false) setUri('') setLabel('') setQueryFirst(true) setPushEvents(true) } catch (err) { console.error('Failed to add cache relay:', err) setError(err instanceof Error ? err.message : 'Connection failed') } finally { setIsLoading(false) } }, [uri, label, queryFirst, pushEvents]) const handleRemove = useCallback((id: string) => { nrcCacheRelayService.remove(id) setCacheRelays(nrcCacheRelayService.getAll()) }, []) const handleToggleEnabled = useCallback((id: string, enabled: boolean) => { nrcCacheRelayService.update(id, { enabled }) setCacheRelays(nrcCacheRelayService.getAll()) }, []) const handleToggleQueryFirst = useCallback((id: string, queryFirst: boolean) => { nrcCacheRelayService.update(id, { queryFirst }) setCacheRelays(nrcCacheRelayService.getAll()) }, []) const handleTogglePushEvents = useCallback((id: string, pushEvents: boolean) => { nrcCacheRelayService.update(id, { pushEvents }) setCacheRelays(nrcCacheRelayService.getAll()) }, []) const handleTest = useCallback(async (id: string, relayUri: string) => { setIsTesting(id) setError(null) try { await nrcCacheRelayService.testConnection(relayUri) nrcCacheRelayService.update(id, { lastConnected: Date.now(), lastError: undefined }) setCacheRelays(nrcCacheRelayService.getAll()) } catch (err) { const errorMsg = err instanceof Error ? err.message : 'Connection failed' nrcCacheRelayService.update(id, { lastError: errorMsg }) setCacheRelays(nrcCacheRelayService.getAll()) } finally { setIsTesting(null) } }, []) if (!pubkey) { return (
{t('Login required to configure cache relays')}
) } return (

{t('Cache relays store events via NRC for faster loading. They are queried first (400ms timeout) before regular relays.')}

{/* Header with Add button */}
{/* Cache Relays List */} {cacheRelays.length === 0 ? (
{t('No cache relays configured')}
) : (
{cacheRelays.map((relay) => (
{relay.label} {relay.lastError && ( ({t('Error')}) )} {relay.lastConnected && !relay.lastError && ( ({t('Connected')}) )}
{relay.uri.length > 60 ? relay.uri.substring(0, 60) + '...' : relay.uri}
handleToggleEnabled(relay.id, checked)} title={t('Enable/Disable')} /> {t('Remove Cache Relay?')} {t('This will remove "{{label}}" from your cache relays.', { label: relay.label })} {t('Cancel')} handleRemove(relay.id)} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {t('Remove')}
{/* Options */}
{relay.lastError && (
{relay.lastError}
)}
))}
)} {/* Add Cache Relay Dialog */} {t('Add Cache Relay')} {t('Add an NRC connection as a cache relay for faster event loading')}
setUri(e.target.value)} placeholder="nostr+relayconnect://..." className="font-mono text-xs" />
setLabel(e.target.value)} placeholder={t('e.g., Home Relay, Personal Cache')} />

{t('Check cache relay before regular relays (400ms timeout)')}

{t('Store loaded events in cache relay in background')}

{error && (
{error}
)}
) }