MessageInfoModal.tsx raw

   1  import {
   2    Dialog,
   3    DialogContent,
   4    DialogHeader,
   5    DialogTitle
   6  } from '@/components/ui/dialog'
   7  import { Button } from '@/components/ui/button'
   8  import dmService from '@/services/dm.service'
   9  import { TDirectMessage } from '@/types'
  10  import { Loader2, RefreshCw, Server } from 'lucide-react'
  11  import { useState } from 'react'
  12  import { useTranslation } from 'react-i18next'
  13  
  14  interface MessageInfoModalProps {
  15    message: TDirectMessage | null
  16    open: boolean
  17    onOpenChange: (open: boolean) => void
  18    onRelaysUpdated?: (relays: string[]) => void
  19  }
  20  
  21  export default function MessageInfoModal({
  22    message,
  23    open,
  24    onOpenChange,
  25    onRelaysUpdated
  26  }: MessageInfoModalProps) {
  27    const { t } = useTranslation()
  28    const [isChecking, setIsChecking] = useState(false)
  29    const [additionalRelays, setAdditionalRelays] = useState<string[]>([])
  30  
  31    if (!message) return null
  32  
  33    const allRelays = [...(message.seenOnRelays || []), ...additionalRelays]
  34    const uniqueRelays = [...new Set(allRelays)]
  35  
  36    const handleCheckOtherRelays = async () => {
  37      setIsChecking(true)
  38      try {
  39        const foundRelays = await dmService.checkOtherRelaysForEvent(
  40          message.id,
  41          uniqueRelays
  42        )
  43        if (foundRelays.length > 0) {
  44          const newRelays = [...additionalRelays, ...foundRelays]
  45          setAdditionalRelays(newRelays)
  46          onRelaysUpdated?.([...(message.seenOnRelays || []), ...newRelays])
  47        }
  48      } catch (error) {
  49        console.error('Failed to check other relays:', error)
  50      } finally {
  51        setIsChecking(false)
  52      }
  53    }
  54  
  55    const formatRelayUrl = (url: string) => {
  56      try {
  57        const parsed = new URL(url)
  58        return parsed.hostname + (parsed.pathname !== '/' ? parsed.pathname : '')
  59      } catch {
  60        return url
  61      }
  62    }
  63  
  64    return (
  65      <Dialog open={open} onOpenChange={onOpenChange}>
  66        <DialogContent className="max-w-sm">
  67          <DialogHeader>
  68            <DialogTitle className="flex items-center gap-2">
  69              <Server className="size-4" />
  70              {t('Message Info')}
  71            </DialogTitle>
  72          </DialogHeader>
  73  
  74          <div className="space-y-4">
  75            {/* Protocol */}
  76            <div>
  77              <span className="text-sm font-medium text-muted-foreground">
  78                {t('Encryption')}
  79              </span>
  80              <p className="text-sm mt-1">
  81                {message.encryptionType === 'nip17' ? 'NIP-44 (Gift Wrap)' : 'NIP-04 (Legacy)'}
  82              </p>
  83            </div>
  84  
  85            {/* Relays */}
  86            <div>
  87              <span className="text-sm font-medium text-muted-foreground">
  88                {t('Seen on relays')}
  89              </span>
  90              {uniqueRelays.length > 0 ? (
  91                <ul className="mt-1 space-y-1">
  92                  {uniqueRelays.map((relay) => (
  93                    <li
  94                      key={relay}
  95                      className="text-sm font-mono bg-muted px-2 py-1 rounded truncate"
  96                      title={relay}
  97                    >
  98                      {formatRelayUrl(relay)}
  99                    </li>
 100                  ))}
 101                </ul>
 102              ) : (
 103                <p className="text-sm text-muted-foreground mt-1">
 104                  {t('No relay information available')}
 105                </p>
 106              )}
 107            </div>
 108  
 109            {/* Check other relays button */}
 110            <Button
 111              variant="outline"
 112              size="sm"
 113              className="w-full"
 114              onClick={handleCheckOtherRelays}
 115              disabled={isChecking}
 116            >
 117              {isChecking ? (
 118                <>
 119                  <Loader2 className="size-4 mr-2 animate-spin" />
 120                  {t('Checking...')}
 121                </>
 122              ) : (
 123                <>
 124                  <RefreshCw className="size-4 mr-2" />
 125                  {t('Check for other relays')}
 126                </>
 127              )}
 128            </Button>
 129          </div>
 130        </DialogContent>
 131      </Dialog>
 132    )
 133  }
 134