import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Checkbox } from '@/components/ui/checkbox' import { Label } from '@/components/ui/label' import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' import { useNostr } from '@/providers/NostrProvider' import client from '@/services/client.service' import indexedDb from '@/services/indexed-db.service' import { TRelayList } from '@/types' import { Check, Loader2, Lock, LockOpen, User, Users, Zap } from 'lucide-react' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' type EncryptionPreference = 'auto' | 'nip04' | 'nip17' interface ConversationSettingsModalProps { partnerPubkey: string | null open: boolean onOpenChange: (open: boolean) => void selectedRelays: string[] onSelectedRelaysChange: (relays: string[]) => void } type RelayInfo = { url: string isYours: boolean isTheirs: boolean isShared: boolean } export default function ConversationSettingsModal({ partnerPubkey, open, onOpenChange, selectedRelays, onSelectedRelaysChange }: ConversationSettingsModalProps) { const { t } = useTranslation() const { pubkey, relayList: myRelayList, hasNip44Support } = useNostr() const [partnerRelayList, setPartnerRelayList] = useState(null) const [isLoading, setIsLoading] = useState(false) const [relays, setRelays] = useState([]) const [encryptionPreference, setEncryptionPreference] = useState('auto') // Fetch partner's relay list when modal opens useEffect(() => { if (!open || !partnerPubkey) return const fetchPartnerRelays = async () => { setIsLoading(true) try { const relayList = await client.fetchRelayList(partnerPubkey) setPartnerRelayList(relayList) } catch (error) { console.error('Failed to fetch partner relay list:', error) } finally { setIsLoading(false) } } fetchPartnerRelays() }, [open, partnerPubkey]) // Load encryption preference when modal opens useEffect(() => { if (!open || !partnerPubkey || !pubkey) return const loadEncryptionPreference = async () => { const saved = await indexedDb.getConversationEncryptionPreference(pubkey, partnerPubkey) setEncryptionPreference(saved || 'auto') } loadEncryptionPreference() }, [open, partnerPubkey, pubkey]) // Save encryption preference when it changes const handleEncryptionChange = async (value: EncryptionPreference) => { setEncryptionPreference(value) if (pubkey && partnerPubkey) { await indexedDb.putConversationEncryptionPreference(pubkey, partnerPubkey, value) } } // Build relay list when data is available useEffect(() => { if (!myRelayList || !partnerRelayList) return const myWriteRelays = new Set(myRelayList.write.map((r) => r.replace(/\/$/, ''))) const theirReadRelays = new Set(partnerRelayList.read.map((r) => r.replace(/\/$/, ''))) // Combine all relays const allRelayUrls = new Set() myRelayList.write.forEach((r) => allRelayUrls.add(r.replace(/\/$/, ''))) partnerRelayList.read.forEach((r) => allRelayUrls.add(r.replace(/\/$/, ''))) const relayInfos: RelayInfo[] = Array.from(allRelayUrls).map((url) => { const normalizedUrl = url.replace(/\/$/, '') const isYours = myWriteRelays.has(normalizedUrl) const isTheirs = theirReadRelays.has(normalizedUrl) return { url, isYours, isTheirs, isShared: isYours && isTheirs } }) // Sort: shared first, then yours, then theirs relayInfos.sort((a, b) => { if (a.isShared && !b.isShared) return -1 if (!a.isShared && b.isShared) return 1 if (a.isYours && !b.isYours) return -1 if (!a.isYours && b.isYours) return 1 return a.url.localeCompare(b.url) }) setRelays(relayInfos) // If no relays selected yet, default to shared relays if (selectedRelays.length === 0) { const sharedRelays = relayInfos.filter((r) => r.isShared).map((r) => r.url) if (sharedRelays.length > 0) { onSelectedRelaysChange(sharedRelays) } } }, [myRelayList, partnerRelayList]) const toggleRelay = (url: string) => { if (selectedRelays.includes(url)) { onSelectedRelaysChange(selectedRelays.filter((r) => r !== url)) } else { onSelectedRelaysChange([...selectedRelays, url]) } } const selectAllShared = () => { const sharedUrls = relays.filter((r) => r.isShared).map((r) => r.url) onSelectedRelaysChange(sharedUrls) } const selectAll = () => { onSelectedRelaysChange(relays.map((r) => r.url)) } const formatRelayUrl = (url: string) => { try { const parsed = new URL(url) return parsed.hostname + (parsed.pathname !== '/' ? parsed.pathname : '') } catch { return url } } if (!partnerPubkey || !pubkey) return null return ( {t('Conversation Settings')}
{/* Encryption Preference */}
handleEncryptionChange(value as EncryptionPreference)} className="grid grid-cols-3 gap-2" >

{encryptionPreference === 'auto' ? t('Matches existing conversation encryption, or sends both on first message') : encryptionPreference === 'nip04' ? t('Classic encryption (NIP-04) - compatible with all clients') : t('Modern encryption (NIP-17) - more private with metadata protection')}

{/* Legend */}
{t('You')}
{t('Them')}
{t('Shared')}
{t('Selected for sending')}
{/* Quick actions */}
{/* Relay list */}
{isLoading ? (
) : relays.length === 0 ? (

{t('No relay information available')}

) : ( relays.map((relay) => (
toggleRelay(relay.url)} > toggleRelay(relay.url)} />
{formatRelayUrl(relay.url)}
{relay.isYours && ( )} {relay.isTheirs && ( )}
)) )}
{/* Info text */}

{t('Selected relays will be used when sending new messages in this conversation.')}

) }