import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { AlertCircle, Search, CheckCircle, Loader2 } from 'lucide-react'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; interface TokenInfo { assetId: number; symbol: string; name: string; decimals: number; exists: boolean; } interface AddTokenModalProps { isOpen: boolean; onClose: () => void; onAddToken: (assetId: number) => Promise; } export const AddTokenModal: React.FC = ({ isOpen, onClose, onAddToken, }) => { const { t } = useTranslation(); const { assetHubApi, isAssetHubReady } = usePezkuwi(); const [assetId, setAssetId] = useState(''); const [error, setError] = useState(''); const [isSearching, setIsSearching] = useState(false); const [isAdding, setIsAdding] = useState(false); const [tokenInfo, setTokenInfo] = useState(null); // Helper to decode hex string to UTF-8 const hexToString = (hex: string): string => { if (!hex || hex === '0x') return ''; try { const hexStr = hex.startsWith('0x') ? hex.slice(2) : hex; const bytes = new Uint8Array(hexStr.match(/.{1,2}/g)?.map(byte => parseInt(byte, 16)) || []); return new TextDecoder('utf-8').decode(bytes).replace(/\0/g, ''); } catch { return ''; } }; const handleSearch = async () => { setError(''); setTokenInfo(null); const id = parseInt(assetId); if (isNaN(id) || id < 0) { setError(t('addToken.invalidId')); return; } if (!assetHubApi || !isAssetHubReady) { setError(t('addToken.notReady')); return; } setIsSearching(true); try { // Check if asset exists const assetInfo = await assetHubApi.query.assets.asset(id); if (!assetInfo || assetInfo.isNone) { setError(t('addToken.notFound', { id })); setIsSearching(false); return; } // Get asset metadata const metadata = await assetHubApi.query.assets.metadata(id); const metaJson = metadata.toJSON() as { symbol?: string; name?: string; decimals?: number }; // Decode hex strings let symbol = metaJson.symbol || ''; let name = metaJson.name || ''; if (typeof symbol === 'string' && symbol.startsWith('0x')) { symbol = hexToString(symbol); } if (typeof name === 'string' && name.startsWith('0x')) { name = hexToString(name); } // Fallback if no metadata if (!symbol) symbol = `Asset #${id}`; if (!name) name = `Unknown Asset`; setTokenInfo({ assetId: id, symbol: symbol.trim(), name: name.trim(), decimals: metaJson.decimals || 12, exists: true, }); } catch (err) { console.error('Failed to fetch asset:', err); setError(t('addToken.fetchFailed')); } finally { setIsSearching(false); } }; const handleAdd = async () => { if (!tokenInfo) return; setIsAdding(true); try { await onAddToken(tokenInfo.assetId); handleClose(); } catch { setError(t('addToken.addFailed')); } finally { setIsAdding(false); } }; const handleClose = () => { setAssetId(''); setError(''); setTokenInfo(null); onClose(); }; const handleKeyPress = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.preventDefault(); handleSearch(); } }; return ( {t('addToken.title')} {t('addToken.description')}
{/* Search Input */}
{ setAssetId(e.target.value); setTokenInfo(null); setError(''); }} onKeyPress={handleKeyPress} placeholder={t('addToken.assetIdPlaceholder')} className="bg-gray-800 border-gray-700 text-white placeholder:text-gray-500 flex-1" min="0" />

{t('addToken.assetIdHelp')}

{/* Token Info Display */} {tokenInfo && (
{t('addToken.found')}
{t('addToken.symbol')}: {tokenInfo.symbol}
{t('addToken.name')}: {tokenInfo.name}
{t('addToken.decimals')}: {tokenInfo.decimals}
{t('addToken.assetId')}: #{tokenInfo.assetId}
)} {/* Error Display */} {error && (

{error}

)} {/* Action Buttons */}
); };